首次提交
This commit is contained in:
40
QXLive/Tools/CKShimmerLabel.h
Executable file
40
QXLive/Tools/CKShimmerLabel.h
Executable file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// CKShimmerLabel.h
|
||||
// CKShimmerLabel
|
||||
//
|
||||
// Created by caokun on 16/8/16.
|
||||
// Copyright © 2016年 caokun. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
ST_LeftToRight, // 从左到右
|
||||
ST_RightToLeft, // 从右到左
|
||||
ST_AutoReverse, // 左右来回
|
||||
ST_ShimmerAll, // 整体闪烁
|
||||
} ShimmerType; // 闪烁类型
|
||||
|
||||
|
||||
@interface CKShimmerLabel : UIView
|
||||
@property (strong, nonatomic) UILabel *contentLabel;
|
||||
// UILabel 常用属性
|
||||
@property (strong, nonatomic) NSString *text;
|
||||
@property (strong, nonatomic) UIFont *font;
|
||||
@property (strong, nonatomic) UIColor *textColor;
|
||||
@property (strong, nonatomic) NSAttributedString *attributedText;
|
||||
@property (assign, nonatomic) NSInteger numberOfLines;
|
||||
|
||||
// CKShimmerLabel 属性
|
||||
@property (assign, nonatomic) ShimmerType shimmerType; // 闪烁类型,默认LeftToRight
|
||||
@property (assign, nonatomic) BOOL repeat; // 循环播放,默认是
|
||||
@property (assign, nonatomic) CGFloat shimmerWidth; // 闪烁宽度,默认20
|
||||
@property (assign, nonatomic) CGFloat shimmerRadius; // 闪烁半径,默认20
|
||||
@property (strong, nonatomic) UIColor *shimmerColor; // 闪烁颜色,默认白
|
||||
@property (assign, nonatomic) NSTimeInterval durationTime; // 持续时间,默认2秒
|
||||
|
||||
- (void)startShimmer; // 开始闪烁,闪烁期间更改上面属性立即生效
|
||||
- (void)stopShimmer; // 停止闪烁
|
||||
|
||||
@end
|
||||
|
||||
324
QXLive/Tools/CKShimmerLabel.m
Executable file
324
QXLive/Tools/CKShimmerLabel.m
Executable file
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// CKShimmerLabel.m
|
||||
// CKShimmerLabel
|
||||
//
|
||||
// Created by caokun on 16/8/16.
|
||||
// Copyright © 2016年 caokun. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CKShimmerLabel.h"
|
||||
|
||||
@interface CKShimmerLabel ()
|
||||
|
||||
|
||||
@property (strong, nonatomic) UILabel *maskLabel;
|
||||
@property (strong, nonatomic) CAGradientLayer *maskLayer;
|
||||
@property (assign, nonatomic) BOOL isPlaying; // 正在播放动画
|
||||
@property (assign, nonatomic) CGSize charSize; // 文字 size
|
||||
@property (assign, nonatomic) CATransform3D startT, endT; // 高亮移动范围 [startT, endT]
|
||||
@property (strong, nonatomic) CABasicAnimation *translate; // 位移动画
|
||||
@property (strong, nonatomic) CABasicAnimation *alphaAni; // alpha 动画
|
||||
|
||||
@end
|
||||
|
||||
@implementation CKShimmerLabel
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self.frame = CGRectMake(0, 0, 60, 30);
|
||||
[self myInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self myInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
if (self = [super initWithCoder:aDecoder]) {
|
||||
[self myInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)myInit {
|
||||
[self addSubview:self.contentLabel];
|
||||
[self addSubview:self.maskLabel];
|
||||
self.layer.masksToBounds = true;
|
||||
self.isPlaying = false;
|
||||
self.startT = CATransform3DIdentity;
|
||||
self.endT = CATransform3DIdentity;
|
||||
self.charSize = CGSizeMake(0, 0);
|
||||
self.shimmerType = ST_LeftToRight;
|
||||
self.repeat = true;
|
||||
self.shimmerWidth = 20;
|
||||
self.shimmerRadius = 20;
|
||||
self.shimmerColor = [UIColor whiteColor];
|
||||
self.durationTime = 2;
|
||||
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
|
||||
// 刷新布局
|
||||
self.contentLabel.frame = self.bounds;
|
||||
self.maskLabel.frame = self.bounds;
|
||||
self.maskLayer.frame = CGRectMake(0, 0, self.charSize.width, self.charSize.height);
|
||||
}
|
||||
|
||||
#pragma mark - 属性 set, get 方法
|
||||
|
||||
- (UILabel *)contentLabel {
|
||||
if (_contentLabel == nil) {
|
||||
_contentLabel = [[UILabel alloc] initWithFrame:self.bounds];
|
||||
_contentLabel.font = [UIFont systemFontOfSize:17];
|
||||
_contentLabel.textColor = [UIColor darkGrayColor];
|
||||
}
|
||||
return _contentLabel;
|
||||
}
|
||||
|
||||
- (UILabel *)maskLabel {
|
||||
if (_maskLabel == nil) {
|
||||
_maskLabel = [[UILabel alloc] initWithFrame:self.bounds];
|
||||
_maskLabel.font = [UIFont systemFontOfSize:17];
|
||||
_maskLabel.textColor = [UIColor darkGrayColor];
|
||||
_maskLabel.hidden = true;
|
||||
}
|
||||
return _maskLabel;
|
||||
}
|
||||
|
||||
- (CALayer *)maskLayer {
|
||||
if (_maskLayer == nil) {
|
||||
_maskLayer = [[CAGradientLayer alloc] init];
|
||||
_maskLayer.backgroundColor = [UIColor clearColor].CGColor;
|
||||
[self freshMaskLayer];
|
||||
}
|
||||
return _maskLayer;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text {
|
||||
if (_text == text) return ;
|
||||
|
||||
_text = text;
|
||||
self.contentLabel.text = text;
|
||||
self.charSize = [self.contentLabel.text boundingRectWithSize:self.contentLabel.frame.size options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.contentLabel.font} context:nil].size;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font {
|
||||
if (_font == font) return ;
|
||||
|
||||
_font = font;
|
||||
self.contentLabel.font = font;
|
||||
self.charSize = [self.contentLabel.text boundingRectWithSize:self.contentLabel.frame.size options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : self.contentLabel.font} context:nil].size;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)textColor {
|
||||
if (_textColor == textColor) return ;
|
||||
|
||||
_textColor = textColor;
|
||||
self.contentLabel.textColor = textColor;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText {
|
||||
if (_attributedText == attributedText) return ;
|
||||
|
||||
_attributedText = attributedText;
|
||||
self.contentLabel.attributedText = attributedText;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setNumberOfLines:(NSInteger)numberOfLines {
|
||||
if (_numberOfLines == numberOfLines) return ;
|
||||
|
||||
_numberOfLines = numberOfLines;
|
||||
self.contentLabel.numberOfLines = numberOfLines;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setShimmerType:(ShimmerType)shimmerType {
|
||||
if (_shimmerType == shimmerType) return ;
|
||||
|
||||
_shimmerType = shimmerType;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setRepeat:(BOOL)repeat {
|
||||
if (_repeat == repeat) return ;
|
||||
|
||||
_repeat = repeat;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setShimmerWidth:(CGFloat)shimmerWidth {
|
||||
if (_shimmerWidth == shimmerWidth) return ;
|
||||
|
||||
_shimmerWidth = shimmerWidth;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setShimmerRadius:(CGFloat)shimmerRadius {
|
||||
if (_shimmerRadius == shimmerRadius) return ;
|
||||
|
||||
_shimmerRadius = shimmerRadius;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setShimmerColor:(UIColor *)shimmerColor {
|
||||
if (_shimmerColor == shimmerColor) return ;
|
||||
|
||||
_shimmerColor = shimmerColor;
|
||||
self.maskLabel.textColor = shimmerColor;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)setDurationTime:(NSTimeInterval)durationTime {
|
||||
if (_durationTime == durationTime) return ;
|
||||
|
||||
_durationTime = durationTime;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)update {
|
||||
if (self.isPlaying) { // 如果在播放动画,更新动画
|
||||
[self stopShimmer];
|
||||
[self startShimmer];
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新 maskLayer 属性值, transform 值
|
||||
- (void)freshMaskLayer {
|
||||
if (self.shimmerType != ST_ShimmerAll) {
|
||||
_maskLayer.backgroundColor = [UIColor clearColor].CGColor;
|
||||
_maskLayer.startPoint = CGPointMake(0, 0.5);
|
||||
_maskLayer.endPoint = CGPointMake(1, 0.5);
|
||||
_maskLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor clearColor].CGColor, (id)[UIColor whiteColor].CGColor, (id)[UIColor whiteColor].CGColor, (id)[UIColor clearColor].CGColor, (id)[UIColor clearColor].CGColor];
|
||||
|
||||
CGFloat w = 1.0;
|
||||
CGFloat sw = 1.0;
|
||||
if (self.charSize.width >= 1) {
|
||||
w = self.shimmerWidth / self.charSize.width * 0.5;
|
||||
sw = self.shimmerRadius / self.charSize.width;
|
||||
}
|
||||
_maskLayer.locations = @[@(0.0), @(0.5 - w - sw), @(0.5 - w), @(0.5 + w), @(0.5 + w + sw), @(1)];
|
||||
CGFloat startX = self.charSize.width * (0.5 - w - sw);
|
||||
CGFloat endX = self.charSize.width * (0.5 + w + sw);
|
||||
self.startT = CATransform3DMakeTranslation(-endX, 0, 1);
|
||||
self.endT = CATransform3DMakeTranslation(self.charSize.width - startX, 0, 1);
|
||||
} else {
|
||||
_maskLayer.backgroundColor = self.shimmerColor.CGColor;
|
||||
_maskLayer.colors = nil;
|
||||
_maskLayer.locations = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 其他方法
|
||||
|
||||
- (void)copyLabel:(UILabel *)dLabel from:(UILabel *)sLabel {
|
||||
dLabel.attributedText = self.attributedText;
|
||||
dLabel.text = self.text;
|
||||
dLabel.font = self.font;
|
||||
dLabel.numberOfLines = self.numberOfLines;
|
||||
}
|
||||
|
||||
- (CABasicAnimation *)translate {
|
||||
if (_translate == nil) {
|
||||
_translate = [CABasicAnimation animationWithKeyPath:@"transform"];
|
||||
}
|
||||
_translate.removedOnCompletion = NO;
|
||||
_translate.duration = self.durationTime;
|
||||
_translate.repeatCount = self.repeat == true ? MAXFLOAT : 0;
|
||||
_translate.autoreverses = self.shimmerType == ST_AutoReverse ? true : false;
|
||||
|
||||
return _translate;
|
||||
}
|
||||
|
||||
- (CABasicAnimation *)alphaAni {
|
||||
if (_alphaAni == nil) {
|
||||
_alphaAni = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||
_alphaAni.repeatCount = MAXFLOAT;
|
||||
_alphaAni.autoreverses = true;
|
||||
_alphaAni.removedOnCompletion = NO;
|
||||
_alphaAni.fromValue = @(0.0);
|
||||
_alphaAni.toValue = @(1.0);
|
||||
}
|
||||
_alphaAni.duration = self.durationTime;
|
||||
|
||||
return _alphaAni;
|
||||
}
|
||||
|
||||
- (void)startShimmer {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 切换到主线程串行队列,下面代码打包成一个事件(原子操作),加到runloop,就不用担心 isPlaying 被多个线程同时修改
|
||||
// dispatch_async() 不 strong 持有本 block,也不用担心循环引用
|
||||
if (self.isPlaying == true) return ;
|
||||
self.isPlaying = true;
|
||||
|
||||
[self copyLabel:self.maskLabel from:self.contentLabel];
|
||||
self.maskLabel.hidden = false;
|
||||
|
||||
// [self.layer addSublayer:self.maskLayer];
|
||||
[self.maskLayer removeFromSuperlayer];
|
||||
[self freshMaskLayer];
|
||||
[self.maskLabel.layer addSublayer:self.maskLayer];
|
||||
self.maskLabel.layer.mask = self.maskLayer;
|
||||
|
||||
switch (self.shimmerType) {
|
||||
case ST_LeftToRight: {
|
||||
self.maskLayer.transform = self.startT;
|
||||
self.translate.fromValue = [NSValue valueWithCATransform3D:self.startT];
|
||||
self.translate.toValue = [NSValue valueWithCATransform3D:self.endT];
|
||||
[self.maskLayer removeAllAnimations];
|
||||
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
||||
break;
|
||||
}
|
||||
case ST_RightToLeft: {
|
||||
self.maskLayer.transform = self.endT;
|
||||
self.translate.fromValue = [NSValue valueWithCATransform3D:self.endT];
|
||||
self.translate.toValue = [NSValue valueWithCATransform3D:self.startT];
|
||||
[self.maskLayer removeAllAnimations];
|
||||
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
||||
break;
|
||||
}
|
||||
case ST_AutoReverse : {
|
||||
self.maskLayer.transform = self.startT;
|
||||
self.translate.fromValue = [NSValue valueWithCATransform3D:self.startT];
|
||||
self.translate.toValue = [NSValue valueWithCATransform3D:self.endT];
|
||||
[self.maskLayer removeAllAnimations];
|
||||
[self.maskLayer addAnimation:self.translate forKey:@"start"];
|
||||
break;
|
||||
}
|
||||
case ST_ShimmerAll : {
|
||||
self.maskLayer.transform = CATransform3DIdentity;
|
||||
[self.maskLayer removeAllAnimations];
|
||||
[self.maskLayer addAnimation:self.alphaAni forKey:@"start"];
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopShimmer {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.isPlaying == false) return ;
|
||||
self.isPlaying = false;
|
||||
|
||||
[self.maskLayer removeAllAnimations];
|
||||
[self.maskLayer removeFromSuperlayer];
|
||||
self.maskLabel.hidden = true;
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
19
QXLive/Tools/Category/NSDate+QX.h
Normal file
19
QXLive/Tools/Category/NSDate+QX.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// NSDate+Category.h
|
||||
// YSDTrucksProject
|
||||
//
|
||||
// Created by 党凯 on 2020/8/20.
|
||||
// Copyright © 2020 党凯. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
|
||||
@interface NSDate (QX)
|
||||
+ (NSString *)timeStringWithTimeInterval:(NSString *)timeStr;
|
||||
@end
|
||||
|
||||
|
||||
151
QXLive/Tools/Category/NSDate+QX.m
Normal file
151
QXLive/Tools/Category/NSDate+QX.m
Normal file
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// NSDate+Category.m
|
||||
// YSDTrucksProject
|
||||
//
|
||||
// Created by 党凯 on 2020/8/20.
|
||||
// Copyright © 2020 党凯. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSDate+QX.h"
|
||||
|
||||
@implementation NSDate (QX)
|
||||
+ (NSString *)timeStringWithTimeInterval:(NSString *)timeInterval
|
||||
{
|
||||
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval.longLongValue]; //此处根据项目需求,选择是否除以1000 , 如果时间戳精确到秒则去掉1000
|
||||
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
|
||||
|
||||
//今天
|
||||
if ([date isToday]) {
|
||||
|
||||
// formatter.dateFormat = @"HH:mm";
|
||||
//
|
||||
// return [formatter stringFromDate:date];
|
||||
return @"今天";
|
||||
}else{
|
||||
|
||||
//昨天
|
||||
if ([date isYesterday]) {
|
||||
|
||||
// formatter.dateFormat = @"昨天HH:mm";
|
||||
// return [formatter stringFromDate:date];
|
||||
|
||||
//一周内 [date weekdayStringFromDate]
|
||||
return @"昨天";
|
||||
}else if ([date isInWeak]){
|
||||
|
||||
// formatter.dateFormat = [NSString stringWithFormat:@"%@%@",[date weekdayStringFromDate],@"HH:mm"];
|
||||
// return [formatter stringFromDate:date];
|
||||
return @"一周内";
|
||||
//直接显示年月日
|
||||
}else{
|
||||
|
||||
// formatter.dateFormat = @"yy-MM-dd HH:mm";
|
||||
formatter.dateFormat = @"yyyy-MM-dd";
|
||||
return [formatter stringFromDate:date];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
//- (BOOL)isInWeek{
|
||||
// NSTimeInterval *time = [self ]
|
||||
//}
|
||||
|
||||
//是否在同一周
|
||||
- (BOOL)isSameWeek
|
||||
{
|
||||
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
int unit = NSCalendarUnitWeekday | NSCalendarUnitMonth | NSCalendarUnitYear ;
|
||||
|
||||
//1.获得当前时间的 年月日
|
||||
NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]];
|
||||
|
||||
//2.获得self
|
||||
NSDateComponents *selfCmps = [calendar components:unit fromDate:self];
|
||||
|
||||
return (selfCmps.year == nowCmps.year) && (selfCmps.month == nowCmps.month) && (selfCmps.day == nowCmps.day);
|
||||
}
|
||||
|
||||
|
||||
//根据日期求星期几
|
||||
- (NSString *)weekdayStringFromDate{
|
||||
|
||||
NSArray *weekdays = [NSArray arrayWithObjects: [NSNull null], @"星期天", @"星期一", @"星期二", @"星期三", @"星期四", @"星期五", @"星期六", nil];
|
||||
|
||||
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
|
||||
|
||||
NSTimeZone *timeZone = [[NSTimeZone alloc] initWithName:@"Asia/Shanghai"];
|
||||
|
||||
[calendar setTimeZone: timeZone];
|
||||
|
||||
NSCalendarUnit calendarUnit = NSCalendarUnitWeekday;
|
||||
|
||||
NSDateComponents *theComponents = [calendar components:calendarUnit fromDate:self];
|
||||
|
||||
return [weekdays objectAtIndex:theComponents.weekday];
|
||||
|
||||
}
|
||||
|
||||
//是否在一周内
|
||||
- (BOOL)isInWeak
|
||||
{
|
||||
//2014-05-01
|
||||
NSDate *nowDate = [[NSDate date] dateWithYMD];
|
||||
|
||||
//2014-04-30
|
||||
NSDate *selfDate = [self dateWithYMD];
|
||||
|
||||
//获得nowDate和selfDate的差距
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSDateComponents *cmps = [calendar components:NSCalendarUnitDay fromDate:selfDate toDate:nowDate options:0];
|
||||
|
||||
return ((cmps.day > 1) && (cmps.day < 7));
|
||||
}
|
||||
|
||||
|
||||
//是否为今天
|
||||
- (BOOL)isToday
|
||||
{
|
||||
//now: 2015-09-05 11:23:00
|
||||
//self 调用这个方法的对象本身
|
||||
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
int unit = NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear ;
|
||||
|
||||
//1.获得当前时间的 年月日
|
||||
NSDateComponents *nowCmps = [calendar components:unit fromDate:[NSDate date]];
|
||||
|
||||
//2.获得self
|
||||
NSDateComponents *selfCmps = [calendar components:unit fromDate:self];
|
||||
|
||||
return (selfCmps.year == nowCmps.year) && (selfCmps.month == nowCmps.month) && (selfCmps.day == nowCmps.day);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//是否为昨天
|
||||
- (BOOL)isYesterday
|
||||
{
|
||||
//2014-05-01
|
||||
NSDate *nowDate = [[NSDate date] dateWithYMD];
|
||||
|
||||
//2014-04-30
|
||||
NSDate *selfDate = [self dateWithYMD];
|
||||
|
||||
//获得nowDate和selfDate的差距
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSDateComponents *cmps = [calendar components:NSCalendarUnitDay fromDate:selfDate toDate:nowDate options:0];
|
||||
|
||||
return cmps.day == 1;
|
||||
}
|
||||
//格式化
|
||||
- (NSDate *)dateWithYMD
|
||||
{
|
||||
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
|
||||
fmt.dateFormat = @"yyyy-MM-dd";
|
||||
NSString *selfStr = [fmt stringFromDate:self];
|
||||
return [fmt dateFromString:selfStr];
|
||||
}
|
||||
@end
|
||||
183
QXLive/Tools/Category/NSString+QX.h
Normal file
183
QXLive/Tools/Category/NSString+QX.h
Normal file
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// NSString+QX.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/7.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSString (QX)
|
||||
/**
|
||||
* 检查是否是有效字符串
|
||||
**/
|
||||
- (BOOL)isExist;
|
||||
|
||||
/**
|
||||
有效数字控制
|
||||
|
||||
@param number 原始数据
|
||||
@return 返回字符串
|
||||
*/
|
||||
+ (NSString *)effectiveNum:(double)number;
|
||||
|
||||
/// 当前字符串是否是数字
|
||||
- (BOOL)isNumber;
|
||||
|
||||
/**
|
||||
获得富文本字符串
|
||||
|
||||
@param attriFont 字体大小
|
||||
@param attriColor 字体颜色
|
||||
@param attriRange 富文本区域
|
||||
@return 富文本字符串
|
||||
*/
|
||||
- (NSMutableAttributedString *)getNSMutableAttributedStringWithAttriFont:(UIFont *)attriFont attriColor:(UIColor *)attriColor isline:(BOOL)isline attriBackgroundColor:(nullable UIColor *)attriBackgroundColor lineSpacing:(CGFloat)lineSpacing attriRange:(NSRange)attriRange;
|
||||
|
||||
/**
|
||||
删除线
|
||||
|
||||
@param textColor 文本颜色
|
||||
@param lineColor 删除线颜色
|
||||
@param range 删除范围
|
||||
@return 富文本字符串
|
||||
*/
|
||||
|
||||
- (NSAttributedString *)deleteLineWithTextColor: (nullable UIColor *)textColor lineColor: (nullable UIColor *)lineColor range: (NSRange)range;
|
||||
- (NSAttributedString *)deleteLineWithTextColor: (nullable UIColor *)textColor lineColor: (nullable UIColor *)lineColor;
|
||||
- (NSAttributedString *)deleteLineWithTextColor: (nullable UIColor *)textColor;
|
||||
- (NSAttributedString *)deleteLine;
|
||||
/**
|
||||
验证有效手机号
|
||||
|
||||
@param phoneNum 手机号
|
||||
@return 是否有效
|
||||
*/
|
||||
+ (BOOL)effectivePhoneNum:(NSString *)phoneNum;
|
||||
|
||||
+ (BOOL)effectivePhoneNum:(NSString *)phoneNum validLength:(NSInteger)length;
|
||||
/**
|
||||
生成uuid
|
||||
追加字符串
|
||||
*/
|
||||
//+ (instancetype)effectivePhoneNumeffectivePhoneNum;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取时间
|
||||
[@"y"]; // 2017
|
||||
[@"yy"]; // 17
|
||||
[@"yyy"]; // 2017
|
||||
[@"yyyy"]; // 2017
|
||||
|
||||
[@"M"]; // 8
|
||||
[@"MM"]; // 08
|
||||
[@"MMM"]; // 8月
|
||||
[@"MMMM"]; // 八月
|
||||
|
||||
[@"d"]; // 3
|
||||
[@"dd"]; // 03
|
||||
[@"D"]; // 215,一年中的第几天
|
||||
|
||||
[@"h"]; // 4
|
||||
[@"hh"]; // 04
|
||||
[@"H"]; // 16 24小时制
|
||||
[@"HH"]; // 16
|
||||
|
||||
[@"m"]; // 28
|
||||
[@"mm"]; // 28
|
||||
[@"s"]; // 57
|
||||
[@"ss"]; // 04
|
||||
|
||||
[@"E"]; // 周四
|
||||
[@"EEEE"]; // 星期四
|
||||
[@"EEEEE"]; // 四
|
||||
[@"e"]; // 5 (显示的是一周的第几天(weekday),1为周日。)
|
||||
[@"ee"]; // 05
|
||||
[@"eee"]; // 周四
|
||||
[@"eeee"]; // 星期四
|
||||
[@"eeeee"]; // 四
|
||||
|
||||
[@"z"]; // GMT+8
|
||||
[@"zzzz"]; // 中国标准时间
|
||||
|
||||
[@"ah"]; // 下午5
|
||||
[@"aH"]; // 下午17
|
||||
[@"am"]; // 下午53
|
||||
[@"as"]; // 下午52
|
||||
*/
|
||||
//+ (NSString *)transformTimeInterval:(NSTimeInterval)sec byFormatter:(NSString *)formatter;
|
||||
//
|
||||
///// 今天显示(时:分)(9:00) 昨日显示(昨天 时:分)(昨天 10:00) 其余显示(年/月/日)(2019/11/06)
|
||||
///// @param sec 时间戳
|
||||
//+ (NSString *)transformTodayYesterdayAndOtherTimeInterval:(NSTimeInterval)sec;
|
||||
/**
|
||||
转换为升序key排列的URL参数格式的key=Value形式的字符串
|
||||
|
||||
@param dict 参数列表
|
||||
@return 格式化后的字符串
|
||||
*/
|
||||
+ (NSString *)transformByAscendingWithDict:(NSDictionary *)dict;
|
||||
|
||||
|
||||
/**
|
||||
判断data的类型
|
||||
|
||||
@return content-Type
|
||||
*/
|
||||
+ (NSString *)contentTypeWithImageData:(NSData *)data;
|
||||
|
||||
|
||||
/// 手机号脱敏
|
||||
- (NSString *)showMoblePhoneNumber;
|
||||
|
||||
- (NSDictionary<NSString *,NSString *> *)parseParameters;
|
||||
|
||||
/// 格式化字符串的长短
|
||||
/// @param limit 限制显示几个字符
|
||||
/// @param suffix 超出部分的显示字符
|
||||
- (NSString *)formatterToLimitNumStrWith:(NSUInteger)limit suffixStr:(nullable NSString *)suffix;
|
||||
- (NSString *)formatterToLimitNumStrWith:(NSUInteger)limit;
|
||||
|
||||
|
||||
/**
|
||||
显示热度值
|
||||
|
||||
@param count 热度值
|
||||
@return 返回字符串
|
||||
*/
|
||||
+ (NSString *)qx_showHotCountNum:(int64_t)count;
|
||||
/// 保留两位
|
||||
+ (NSString *)qx_showHotCountNumDouble:(int64_t)count ;
|
||||
/**
|
||||
* 显示房间名字
|
||||
*/
|
||||
- (NSString *)qx_showRoomName;
|
||||
|
||||
/**
|
||||
订单倒计时
|
||||
type == 1 返回 00:00:00
|
||||
type == 2 返回 00时00分00秒
|
||||
*/
|
||||
|
||||
+ (NSString *)timeStrFromInterval:(long)lastTime type:(NSInteger)type;
|
||||
|
||||
+ (BOOL)inputShouldLetterOrNum:(NSString *)inputString;
|
||||
|
||||
|
||||
+ (NSString *)safeString:(NSString *)string ByAppendingString:(NSString *)aString;
|
||||
|
||||
/**
|
||||
超过9999数量用w显示并保留1位小数(如14500 = 1.5w)
|
||||
*/
|
||||
- (NSString *)tenThousandFormatString;
|
||||
|
||||
- (NSInteger)ageWithDateOfBirth;
|
||||
/// 传入秒 得到 几天 几小时
|
||||
+ (NSString*)getTimeWithSecond:(long long)second;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
405
QXLive/Tools/Category/NSString+QX.m
Normal file
405
QXLive/Tools/Category/NSString+QX.m
Normal file
@@ -0,0 +1,405 @@
|
||||
//
|
||||
// NSString+QX.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/7.
|
||||
//
|
||||
|
||||
#import "NSString+QX.h"
|
||||
#import "NSDate+BRPickerView.h"
|
||||
@implementation NSString (QX)
|
||||
- (BOOL)isExist {
|
||||
if (!self) {
|
||||
return NO;
|
||||
}
|
||||
if ([self isKindOfClass:[NSNull class]]) {
|
||||
return NO;
|
||||
}
|
||||
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
||||
NSString *trimmedStr = [self stringByTrimmingCharactersInSet:set];
|
||||
if (trimmedStr.length == 0 || trimmedStr.length == NSNotFound) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
有效数字控制
|
||||
|
||||
@param number 原始数据
|
||||
@return 返回字符串
|
||||
*/
|
||||
+ (NSString *)effectiveNum:(double)number {
|
||||
if (fmodf(number, 1) == 0 || fmodf(number * 10, 1) == 0) {
|
||||
return [NSString notRounding:number afterPoint:1];
|
||||
}else{
|
||||
return [NSString notRounding:number afterPoint:2];
|
||||
}
|
||||
}
|
||||
// 转换不四舍五入
|
||||
+ (NSString *)notRounding:(double)price afterPoint:(int)position{
|
||||
NSDecimalNumberHandler* roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:position raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
|
||||
NSDecimalNumber *ouncesDecimal;
|
||||
NSDecimalNumber *roundedOunces;
|
||||
ouncesDecimal = [[NSDecimalNumber alloc] initWithDouble:price];
|
||||
roundedOunces = [ouncesDecimal decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
|
||||
return [NSString stringWithFormat:@"%@",roundedOunces];
|
||||
}
|
||||
|
||||
- (BOOL)isNumber {
|
||||
if (self.length ==0 ) {
|
||||
return NO;
|
||||
}
|
||||
unichar c;
|
||||
for (int i=0; i<self.length; i++) {
|
||||
c=[self characterAtIndex:i];
|
||||
if (!isdigit(c)) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getNSMutableAttributedStringWithAttriFont:(UIFont *)attriFont attriColor:(UIColor *)attriColor isline:(BOOL)isline attriBackgroundColor:(UIColor *)attriBackgroundColor lineSpacing:(CGFloat)lineSpacing attriRange:(NSRange)attriRange {
|
||||
NSMutableAttributedString *getStr = [[NSMutableAttributedString alloc]initWithString:self];
|
||||
|
||||
NSMutableDictionary <NSAttributedStringKey, id>*dict = [NSMutableDictionary dictionaryWithCapacity:1];
|
||||
dict[NSFontAttributeName] = attriFont;
|
||||
dict[NSForegroundColorAttributeName] = attriColor;
|
||||
|
||||
if (isline) {
|
||||
dict[NSStrikethroughStyleAttributeName] = [NSNumber numberWithInteger:NSUnderlineStyleSingle];
|
||||
}
|
||||
if (attriBackgroundColor) {
|
||||
dict[NSBackgroundColorAttributeName] = attriBackgroundColor;
|
||||
}
|
||||
if (lineSpacing > 0) {//行间距
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.lineSpacing = lineSpacing;
|
||||
dict[NSParagraphStyleAttributeName] = paragraphStyle;
|
||||
}
|
||||
|
||||
[getStr addAttributes:dict range:attriRange];
|
||||
|
||||
return getStr;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)deleteLineWithTextColor:(UIColor *)textColor lineColor:(UIColor *)lineColor range:(NSRange)range {
|
||||
if (!self || self.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:self
|
||||
attributes:@{NSStrikethroughStyleAttributeName: @(NSUnderlineStyleNone)}];
|
||||
[attrStr setAttributes:@{NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle),
|
||||
NSBaselineOffsetAttributeName: @0}
|
||||
range:range];
|
||||
if (textColor) {
|
||||
[attrStr addAttribute:NSForegroundColorAttributeName value:textColor range: range];
|
||||
}
|
||||
if (lineColor) {
|
||||
[attrStr addAttribute:NSStrikethroughColorAttributeName value:lineColor range:range];
|
||||
}
|
||||
return attrStr;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)deleteLineWithTextColor:(UIColor *)textColor lineColor:(UIColor *)lineColor {
|
||||
return [self deleteLineWithTextColor:textColor lineColor:lineColor range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)deleteLineWithTextColor:(UIColor *)textColor {
|
||||
return [self deleteLineWithTextColor:textColor lineColor:textColor range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)deleteLine {
|
||||
return [self deleteLineWithTextColor:nil lineColor:nil range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
+ (BOOL)effectivePhoneNum:(NSString *)phoneNum {
|
||||
return [self effectivePhoneNum:phoneNum validLength:11];
|
||||
}
|
||||
|
||||
+ (BOOL)effectivePhoneNum:(NSString *)phoneNum validLength:(NSInteger)length {
|
||||
NSScanner* scan = [NSScanner scannerWithString:phoneNum];
|
||||
int val;
|
||||
return [scan scanInt:&val] && [scan isAtEnd] && phoneNum.length == length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+ (instancetype)generateUUIDString {
|
||||
CFUUIDRef puuid = CFUUIDCreate(nil);
|
||||
CFStringRef uuidString = CFUUIDCreateString(nil, puuid);
|
||||
NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
|
||||
CFRelease(puuid);
|
||||
CFRelease(uuidString);
|
||||
return result;
|
||||
}
|
||||
|
||||
//+ (NSString *)transformTimeInterval:(NSTimeInterval)sec byFormatter:(NSString *)formatter {
|
||||
// NSDate * dt = [NSDate dateWithTimeIntervalSince1970:sec];
|
||||
// NSDateFormatter * df = [NSDateFormatter shared];
|
||||
// [df setDateFormat:formatter];
|
||||
// NSString *regStr = [df stringFromDate:dt];
|
||||
// return regStr;
|
||||
//}
|
||||
//
|
||||
//+ (NSString *)transformTodayYesterdayAndOtherTimeInterval:(NSTimeInterval)sec {
|
||||
// NSDate *dt = [NSDate dateWithTimeIntervalSince1970:sec];
|
||||
// NSCalendar *calendar =[NSCalendar currentCalendar];
|
||||
// NSDateFormatter * df = [NSDateFormatter shared];
|
||||
// NSString *prefix = @"";
|
||||
// if ([calendar isDateInToday:dt]) {
|
||||
// df.dateFormat = @"HH:mm";
|
||||
// }else if ([calendar isDateInYesterday:dt]) {
|
||||
// prefix = @"昨天 ";
|
||||
// df.dateFormat = @"HH:mm";
|
||||
// }else {
|
||||
// df.dateFormat = @"yyyy/MM/dd";
|
||||
// }
|
||||
// return [NSString stringWithFormat:@"%@%@",prefix,[df stringFromDate:dt]];
|
||||
//}
|
||||
|
||||
|
||||
+ (NSString *)transformByAscendingWithDict:(NSDictionary *)dict {
|
||||
NSArray*keys = [dict allKeys];
|
||||
|
||||
NSArray*sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) {
|
||||
return[obj1 compare:obj2 options:NSNumericSearch];//正序
|
||||
}];
|
||||
|
||||
NSString*str =@"";
|
||||
|
||||
for(NSString*key in sortedArray) {
|
||||
|
||||
id value = [dict objectForKey:key];
|
||||
|
||||
if([value isKindOfClass:[NSDictionary class]]) {
|
||||
|
||||
value = [self transformByAscendingWithDict:value];
|
||||
|
||||
}
|
||||
|
||||
if([str length] !=0) {
|
||||
|
||||
str = [str stringByAppendingString:@"&"];
|
||||
|
||||
}
|
||||
|
||||
str = [str stringByAppendingFormat:@"%@=%@",key,value];
|
||||
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
+ (NSString *)contentTypeWithImageData:(NSData *)data {
|
||||
if (!data) {
|
||||
return @"jpeg";
|
||||
}
|
||||
|
||||
// File signatures table: http://www.garykessler.net/library/file_sigs.html
|
||||
uint8_t c;
|
||||
[data getBytes:&c length:1];
|
||||
switch (c) {
|
||||
case 0xFF:
|
||||
return @"jpeg";
|
||||
case 0x89:
|
||||
return @"png";
|
||||
case 0x47:
|
||||
return @"gif";
|
||||
case 0x49:
|
||||
case 0x4D:
|
||||
return @"tiff";
|
||||
case 0x52: {
|
||||
if (data.length >= 12) {
|
||||
//RIFF....WEBP
|
||||
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
|
||||
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
|
||||
return @"webp";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x00: {
|
||||
if (data.length >= 12) {
|
||||
//....ftypheic ....ftypheix ....ftyphevc ....ftyphevx
|
||||
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(4, 8)] encoding:NSASCIIStringEncoding];
|
||||
if ([testString isEqualToString:@"ftypheic"]
|
||||
|| [testString isEqualToString:@"ftypheix"]
|
||||
|| [testString isEqualToString:@"ftyphevc"]
|
||||
|| [testString isEqualToString:@"ftyphevx"]) {
|
||||
return @"HEIC";
|
||||
}
|
||||
//....ftypmif1 ....ftypmsf1
|
||||
if ([testString isEqualToString:@"ftypmif1"] || [testString isEqualToString:@"ftypmsf1"]) {
|
||||
return @"HEIF";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return @"jpeg";
|
||||
}
|
||||
|
||||
- (NSString *)showMoblePhoneNumber {
|
||||
if (self.length >= 7) {
|
||||
return [self stringByReplacingCharactersInRange:NSMakeRange(self.length-8, 4) withString:@"****"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *,NSString *> *)parseParameters{
|
||||
NSMutableDictionary *parm = [NSMutableDictionary dictionary];
|
||||
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:self];
|
||||
//回调遍历所有参数,添加入字典
|
||||
[urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
parm[obj.name] = obj.value;
|
||||
}];
|
||||
return parm;
|
||||
|
||||
}
|
||||
|
||||
- (NSString *)formatterToLimitNumStrWith:(NSUInteger)limit suffixStr:(NSString *)suffix {
|
||||
if (self.length > limit) {
|
||||
return [NSString stringWithFormat:@"%@%@",[self substringToIndex:limit],suffix ?: @""];
|
||||
}else{
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)formatterToLimitNumStrWith:(NSUInteger)limit {
|
||||
return [self formatterToLimitNumStrWith:limit suffixStr:nil];
|
||||
}
|
||||
|
||||
|
||||
|
||||
+ (NSString *)qx_showHotCountNum:(int64_t)count {
|
||||
if (count > 9999 || count < -9999) {
|
||||
// return [NSString stringWithFormat:@"%@w",[self effectiveNum:(double)count/10000.0]];
|
||||
return [NSString stringWithFormat:@"%.2fw",(double)count/10000.0];
|
||||
}else {
|
||||
return [NSString stringWithFormat:@"%lld",count];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+ (NSString *)qx_showHotCountNumDouble:(int64_t)count {
|
||||
if (count > 9999 || count < -9999) {
|
||||
// return [NSString stringWithFormat:@"%@w",[self effectiveNum:(double)count/10000.0]];
|
||||
return [NSString stringWithFormat:@"%.2fw",(double)count/10000.0];
|
||||
}else {
|
||||
return [NSString stringWithFormat:@"%lld",count];
|
||||
}
|
||||
}
|
||||
- (NSString *)qx_showRoomName {
|
||||
if (self.length > 10) {
|
||||
return [NSString stringWithFormat:@"%@...",[self formatterToLimitNumStrWith:10]];
|
||||
}else {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)timeStrFromInterval:(long)lastTime type:(NSInteger)type{
|
||||
if (lastTime < 60) {
|
||||
NSString *secondStr = [NSString stringWithFormat:@"%ld",lastTime];
|
||||
if (lastTime < 10) {
|
||||
secondStr = [NSString stringWithFormat:@"0%@",secondStr];
|
||||
}
|
||||
if (type == 1) {
|
||||
return [NSString stringWithFormat:@"00:%@",secondStr];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%ld秒",lastTime];
|
||||
}
|
||||
if (lastTime < 3600) {
|
||||
NSString *minuteStr = [NSString stringWithFormat:@"%ld",lastTime / 60];
|
||||
NSString *secondStr = [NSString stringWithFormat:@"%ld",lastTime % 60];
|
||||
if (minuteStr.length == 1) {
|
||||
minuteStr = [NSString stringWithFormat:@"0%@",minuteStr];
|
||||
}
|
||||
if (secondStr.length == 1) {
|
||||
secondStr = [NSString stringWithFormat:@"0%@",secondStr];
|
||||
}
|
||||
if (type == 1) {
|
||||
return [NSString stringWithFormat:@"%@:%@",minuteStr,secondStr];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%ld分%ld秒",lastTime / 60,lastTime % 60];
|
||||
}
|
||||
NSString *hourStr = [NSString stringWithFormat:@"%ld",lastTime / 3600];
|
||||
NSString *minuteStr = [NSString stringWithFormat:@"%ld",(lastTime % 3600) / 60];
|
||||
if (minuteStr.length == 1) {
|
||||
minuteStr = [NSString stringWithFormat:@"0%@",minuteStr];
|
||||
}
|
||||
if (type == 1) {
|
||||
return [NSString stringWithFormat:@"%@:%@",hourStr,minuteStr];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%ld时%ld分",lastTime / 3600,(lastTime % 3600) / 60];
|
||||
}
|
||||
|
||||
+ (BOOL)inputShouldLetterOrNum:(NSString *)inputString {
|
||||
if (inputString.length == 0) return NO;
|
||||
NSString *regex =@"[a-zA-Z0-9]*";
|
||||
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
|
||||
return [pred evaluateWithObject:inputString];
|
||||
}
|
||||
|
||||
+ (NSString *)safeString:(NSString *)string ByAppendingString:(NSString *)aString{
|
||||
if ([string isKindOfClass:[NSString class]]) {
|
||||
if (![string isExist]) {
|
||||
string = @"";
|
||||
}
|
||||
}else {
|
||||
string = @"";
|
||||
}
|
||||
if ([aString isKindOfClass:[NSString class]]) {
|
||||
if (![aString isExist]) {
|
||||
aString = @"";
|
||||
}
|
||||
}else {
|
||||
aString = @"";
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@%@",string,aString];
|
||||
}
|
||||
|
||||
- (NSString *)tenThousandFormatString {
|
||||
NSInteger number = [self integerValue];
|
||||
if (number < 10000) {
|
||||
return number < 0 ? @"0" : [NSString stringWithFormat:@"%ld",(long)number];
|
||||
}else {
|
||||
double temp = number / 10000.0;
|
||||
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:1 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
|
||||
NSDecimalNumber *decimal = [[NSDecimalNumber alloc] initWithDouble:temp];
|
||||
NSNumber *ratio = [decimal decimalNumberByRoundingAccordingToBehavior:roundingBehavior];
|
||||
return [[NSNumberFormatter localizedStringFromNumber:ratio numberStyle:NSNumberFormatterDecimalStyle] stringByAppendingString:@"w"];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)ageWithDateOfBirth{
|
||||
NSDate *date = [NSDate br_dateFromString:self dateFormat:@"yyyy-MM-dd"];
|
||||
// 出生日期转换 年月日
|
||||
NSDateComponents *components1 = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
|
||||
NSInteger brithDateYear = [components1 year];
|
||||
NSInteger brithDateDay = [components1 day];
|
||||
NSInteger brithDateMonth = [components1 month];
|
||||
|
||||
// 获取系统当前 年月日
|
||||
NSDateComponents *components2 = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
|
||||
NSInteger currentDateYear = [components2 year];
|
||||
NSInteger currentDateDay = [components2 day];
|
||||
NSInteger currentDateMonth = [components2 month];
|
||||
|
||||
// 计算年龄
|
||||
NSInteger iAge = currentDateYear - brithDateYear - 1;
|
||||
if ((currentDateMonth > brithDateMonth) || (currentDateMonth == brithDateMonth && currentDateDay >= brithDateDay)) {
|
||||
iAge++;
|
||||
}
|
||||
|
||||
return iAge;
|
||||
}
|
||||
+(NSString *)getTimeWithSecond:(long long)second{
|
||||
NSInteger day = second/(60*60*24);
|
||||
NSInteger hour = second%(60*60*24)/(60*60);
|
||||
return [NSString stringWithFormat:@"%ld天%ld小时",day,hour];
|
||||
}
|
||||
@end
|
||||
34
QXLive/Tools/Category/UIButton+QX.h
Normal file
34
QXLive/Tools/Category/UIButton+QX.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// UIButton+QX.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
typedef NS_ENUM(NSUInteger, QXButtonEdgeInsetsStyle) {
|
||||
QXButtonEdgeInsetsStyleTop, ///< image在上,label在下
|
||||
QXButtonEdgeInsetsStyleLeft, ///< image在左,label在右
|
||||
QXButtonEdgeInsetsStyleBottom, ///< image在下,label在上
|
||||
QXButtonEdgeInsetsStyleRight ///< image在右,label在左
|
||||
};
|
||||
@interface UIButton (QX)
|
||||
|
||||
/**
|
||||
设置button的titleLabel和imageView的布局样式及间距
|
||||
|
||||
@param style titleLabel和imageView的布局样式
|
||||
@param space titleLabel和imageView的b间距
|
||||
*/
|
||||
- (void)qx_layoutButtonWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyle)style
|
||||
imageTitleSpace:(CGFloat)space;
|
||||
|
||||
- (void)qx_layoutButtonNOSizeToFitWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyle)style
|
||||
imageTitleSpace:(CGFloat)space;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
127
QXLive/Tools/Category/UIButton+QX.m
Normal file
127
QXLive/Tools/Category/UIButton+QX.m
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// UIButton+QX.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import "UIButton+QX.h"
|
||||
|
||||
@implementation UIButton (QX)
|
||||
- (void)qx_layoutButtonWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyle)style
|
||||
imageTitleSpace:(CGFloat)space{
|
||||
[self sizeToFit];
|
||||
[self qx_layoutButtonNOSizeToFitWithEdgeInsetsStyle:style imageTitleSpace:space];
|
||||
}
|
||||
|
||||
- (void)qx_layoutButtonNOSizeToFitWithEdgeInsetsStyle:(QXButtonEdgeInsetsStyle)style
|
||||
imageTitleSpace:(CGFloat)space {
|
||||
/**
|
||||
* 前置知识点:titleEdgeInsets是title相对于其上下左右的inset,跟tableView的contentInset是类似的,
|
||||
* 如果只有title,那它的上下左右都是相对于button的,image也是一样;
|
||||
* 如果同时有image和label,那这时候image的上左下是相对于button,右边是相对于label的;title的上右下是相对于button,左边是相对于image的。
|
||||
*/
|
||||
|
||||
CGFloat spacef = ceilf(space);
|
||||
|
||||
|
||||
// 1.得到imageView和titleLabel的宽、高
|
||||
CGFloat imageWidth = self.imageView.frame.size.width;
|
||||
CGFloat imageHeight = self.imageView.frame.size.height;
|
||||
|
||||
CGFloat labelWidth = 0.0;
|
||||
CGFloat labelHeight = 0.0;
|
||||
if (@available(iOS 8.0, *)) {
|
||||
// 由于iOS8中titleLabel的size为0,用下面的设置
|
||||
labelWidth = self.titleLabel.intrinsicContentSize.width;
|
||||
labelHeight = self.titleLabel.intrinsicContentSize.height;
|
||||
}else {
|
||||
labelWidth = self.titleLabel.frame.size.width;
|
||||
labelHeight = self.titleLabel.frame.size.height;
|
||||
}
|
||||
|
||||
// 2.声明全局的imageEdgeInsets和labelEdgeInsets
|
||||
UIEdgeInsets imageEdgeInsets = UIEdgeInsetsZero;
|
||||
UIEdgeInsets labelEdgeInsets = UIEdgeInsetsZero;
|
||||
UIEdgeInsets contentEdgeInsets = UIEdgeInsetsZero;
|
||||
|
||||
CGFloat imageOffSetX = labelWidth / 2.0;
|
||||
CGFloat labelOffSetX = imageWidth / 2.0;
|
||||
|
||||
CGFloat maxWidth = MAX(imageWidth,labelWidth); // 上下排布宽度肯定变小 获取最大宽度的那个
|
||||
CGFloat changeWidth = imageWidth + labelWidth - maxWidth; // 横向缩小的总宽度
|
||||
CGFloat maxHeight = MAX(imageHeight,labelHeight); // 获取最大高度那个 (就是水平默认排布的时候的原始高度)
|
||||
|
||||
// 3.根据style和spacef得到imageEdgeInsets和labelEdgeInsets的值
|
||||
switch (style) {
|
||||
case QXButtonEdgeInsetsStyleTop:
|
||||
{
|
||||
CGFloat gap = (maxHeight - MIN(imageHeight, labelHeight))/2.0;
|
||||
if (imageHeight >= labelHeight) {
|
||||
imageEdgeInsets = UIEdgeInsetsMake(0, imageOffSetX, 0, -imageOffSetX);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(labelHeight + gap + spacef, -labelOffSetX, -(labelHeight + gap + spacef), labelOffSetX);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(0, - changeWidth / 2.0, spacef + labelHeight, -changeWidth / 2.0);
|
||||
}else{
|
||||
imageEdgeInsets = UIEdgeInsetsMake(-(gap + imageHeight + spacef), imageOffSetX, gap + imageHeight + spacef, -imageOffSetX);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(0, -labelOffSetX, 0, labelOffSetX);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(spacef + imageHeight, - changeWidth / 2.0, 0, -changeWidth / 2.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QXButtonEdgeInsetsStyleLeft:
|
||||
{
|
||||
imageEdgeInsets = UIEdgeInsetsMake(0, -spacef/2.0, 0, spacef/2.0);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(0, spacef/2.0, 0, -spacef/2.0);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(0, spacef/2.0, 0, spacef/2.0);
|
||||
}
|
||||
break;
|
||||
case QXButtonEdgeInsetsStyleBottom:
|
||||
{
|
||||
CGFloat gap = (maxHeight - MIN(imageHeight, labelHeight))/2.0;
|
||||
if (imageHeight >= labelHeight) {
|
||||
imageEdgeInsets = UIEdgeInsetsMake(0,
|
||||
imageOffSetX,
|
||||
0,
|
||||
-imageOffSetX);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(-(labelHeight + gap + spacef),
|
||||
-labelOffSetX,
|
||||
labelHeight + gap + spacef,
|
||||
labelOffSetX);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(spacef + labelHeight,
|
||||
- changeWidth / 2.0,
|
||||
0,
|
||||
-changeWidth / 2.0);
|
||||
}else{
|
||||
imageEdgeInsets = UIEdgeInsetsMake(gap + imageHeight + spacef,
|
||||
imageOffSetX,
|
||||
-(gap + imageHeight + spacef),
|
||||
-imageOffSetX);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(0,
|
||||
-labelOffSetX,
|
||||
0,
|
||||
labelOffSetX);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(0,
|
||||
- changeWidth / 2.0,
|
||||
spacef + imageHeight,
|
||||
-changeWidth / 2.0);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case QXButtonEdgeInsetsStyleRight:
|
||||
{
|
||||
imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth+spacef/2.0, 0, -labelWidth-spacef/2.0);
|
||||
labelEdgeInsets = UIEdgeInsetsMake(0, -imageWidth-spacef/2.0, 0, imageWidth+spacef/2.0);
|
||||
contentEdgeInsets = UIEdgeInsetsMake(0, spacef/2.0, 0, spacef/2.0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 4.赋值
|
||||
self.titleEdgeInsets = labelEdgeInsets;
|
||||
self.imageEdgeInsets = imageEdgeInsets;
|
||||
self.contentEdgeInsets = contentEdgeInsets;
|
||||
}
|
||||
@end
|
||||
25
QXLive/Tools/Category/UIColor+QX.h
Executable file
25
QXLive/Tools/Category/UIColor+QX.h
Executable file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// UIColor+Hex.h
|
||||
// OCThemeDemo
|
||||
//
|
||||
// Created by 阿飞 on 2019/5/7.
|
||||
// Copyright © 2020 阿飞. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIColor (QX)
|
||||
|
||||
//随机色
|
||||
+ (UIColor *)sh_colorArc4random;
|
||||
|
||||
//从十六进制字符串获取颜色,
|
||||
+ (UIColor *)sh_colorWithHexString:(NSString *)color;
|
||||
|
||||
//color:支持@“#123456”、 @“0X123456”、 @“123456”三种格式
|
||||
+ (UIColor *)sh_colorWithHexString:(NSString *)color alpha:(CGFloat)alpha;
|
||||
|
||||
//渐变色
|
||||
+ (UIColor *)sh_getColorOfPercent:(CGFloat)percent between:(UIColor *)color1 and:(UIColor *)color2;
|
||||
|
||||
@end
|
||||
94
QXLive/Tools/Category/UIColor+QX.m
Executable file
94
QXLive/Tools/Category/UIColor+QX.m
Executable file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// UIColor+Hex.m
|
||||
// OCThemeDemo
|
||||
//
|
||||
// Created by 阿飞 on 2019/5/7.
|
||||
// Copyright © 2020 阿飞. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIColor+QX.h"
|
||||
|
||||
@implementation UIColor (QX)
|
||||
|
||||
+ (UIColor *)sh_colorWithHexString:(NSString *)color alpha:(CGFloat)alpha {
|
||||
//删除字符串中的空格
|
||||
NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
|
||||
// String should be 6 or 8 characters
|
||||
if ([cString length] < 6)
|
||||
{
|
||||
return [UIColor clearColor];
|
||||
}
|
||||
// strip 0X if it appears
|
||||
//如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
|
||||
if ([cString hasPrefix:@"0X"])
|
||||
{
|
||||
cString = [cString substringFromIndex:2];
|
||||
}
|
||||
//如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
|
||||
if ([cString hasPrefix:@"#"])
|
||||
{
|
||||
cString = [cString substringFromIndex:1];
|
||||
}
|
||||
if ([cString length] != 6)
|
||||
{
|
||||
return [UIColor clearColor];
|
||||
}
|
||||
|
||||
// Separate into r, g, b substrings
|
||||
NSRange range;
|
||||
range.location = 0;
|
||||
range.length = 2;
|
||||
//r
|
||||
NSString *rString = [cString substringWithRange:range];
|
||||
//g
|
||||
range.location = 2;
|
||||
NSString *gString = [cString substringWithRange:range];
|
||||
//b
|
||||
range.location = 4;
|
||||
NSString *bString = [cString substringWithRange:range];
|
||||
|
||||
// Scan values
|
||||
unsigned int r, g, b;
|
||||
[[NSScanner scannerWithString:rString] scanHexInt:&r];
|
||||
[[NSScanner scannerWithString:gString] scanHexInt:&g];
|
||||
[[NSScanner scannerWithString:bString] scanHexInt:&b];
|
||||
return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
|
||||
}
|
||||
|
||||
//默认alpha值为1
|
||||
+ (UIColor *)sh_colorWithHexString:(NSString *)color {
|
||||
return [self sh_colorWithHexString:color alpha:1.0f];
|
||||
}
|
||||
|
||||
+ (UIColor *)sh_colorArc4random {
|
||||
|
||||
float red = arc4random()%256 / 255.0;
|
||||
float bule = arc4random()%256 / 255.0;
|
||||
float green = arc4random()%256 / 255.0;
|
||||
|
||||
return [UIColor colorWithRed:red green:green blue:bule alpha:1.0];
|
||||
}
|
||||
|
||||
#pragma mark - 两种颜色之间的渐变
|
||||
/**
|
||||
* 两种颜色之间的渐变
|
||||
*
|
||||
* @param percent 渐变百分率
|
||||
* @param color1 颜色1(高亮颜色)
|
||||
* @param color2 颜色2(普通状态颜色)
|
||||
*
|
||||
*/
|
||||
+ (UIColor *)sh_getColorOfPercent:(CGFloat)percent between:(UIColor *)color1 and:(UIColor *)color2{
|
||||
CGFloat red1, green1, blue1, alpha1;
|
||||
[color1 getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
|
||||
|
||||
CGFloat red2, green2, blue2, alpha2;
|
||||
[color2 getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];
|
||||
|
||||
CGFloat p1 = percent;
|
||||
CGFloat p2 = 1.0 - percent;
|
||||
UIColor *mid = [UIColor colorWithRed:red1*p1+red2*p2 green:green1*p1+green2*p2 blue:blue1*p1+blue2*p2 alpha:1.0f];
|
||||
return mid;
|
||||
}
|
||||
|
||||
@end
|
||||
24
QXLive/Tools/Category/UIControl+QX.h
Normal file
24
QXLive/Tools/Category/UIControl+QX.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// UIControl+QX.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/19.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval";
|
||||
static const void *BandNameKey = &BandNameKey;
|
||||
|
||||
|
||||
@interface UIControl (QX)
|
||||
|
||||
@property (nonatomic, assign)NSTimeInterval needEventInterval; /* 多少秒后再次响应 */
|
||||
@property (nonatomic, assign, readonly)BOOL ignoreEvent; /*查看当前是否已经停止响应 如果停止响应为YES, 可以再次交互响应为NO; 不是固定的, 取到的值是动态变化的 和needEventIntervaly有关*/
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
86
QXLive/Tools/Category/UIControl+QX.m
Normal file
86
QXLive/Tools/Category/UIControl+QX.m
Normal file
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// UIControl+QX.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/19.
|
||||
//
|
||||
|
||||
#import "UIControl+QX.h"
|
||||
static inline void swizzling_exchangeMethodWithSelector(Class clazz, SEL originalSelector, SEL exchangeSelector) {
|
||||
// 获取原方法
|
||||
Method originalMethod = class_getInstanceMethod(clazz, originalSelector);
|
||||
|
||||
// 获取需要交换的方法
|
||||
Method exchangeMethod = class_getInstanceMethod(clazz, exchangeSelector);
|
||||
|
||||
if (!originalMethod || !exchangeMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (class_addMethod(clazz, originalSelector, method_getImplementation(exchangeMethod), method_getTypeEncoding(exchangeMethod))) {
|
||||
class_replaceMethod(clazz, exchangeSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
|
||||
}else{
|
||||
method_exchangeImplementations(originalMethod, exchangeMethod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void swizzling_exchangeMethodWithMethod(Method originalMethod, Method exchangeMethod) {
|
||||
method_exchangeImplementations(originalMethod, exchangeMethod);
|
||||
}
|
||||
|
||||
@implementation UIControl (QX)
|
||||
|
||||
|
||||
- (NSTimeInterval)needEventInterval
|
||||
{
|
||||
|
||||
return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue];
|
||||
|
||||
}
|
||||
|
||||
- (void)setNeedEventInterval:(NSTimeInterval)needEventInterval
|
||||
{
|
||||
objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(needEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)ignoreEvent
|
||||
{
|
||||
return [objc_getAssociatedObject(self, BandNameKey) boolValue];
|
||||
}
|
||||
|
||||
- (void)setIgnoreEvent:(BOOL)ignoreEvent
|
||||
{
|
||||
objc_setAssociatedObject(self, BandNameKey, [NSNumber numberWithBool:ignoreEvent], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
|
||||
+(void)load
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
swizzling_exchangeMethodWithSelector([self class],
|
||||
@selector(sendAction:to:forEvent:),
|
||||
@selector(__uxy_sendAction:to:forEvent:));
|
||||
});
|
||||
}
|
||||
|
||||
- (void)__uxy_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
|
||||
{
|
||||
|
||||
if (self.ignoreEvent) return; /*当ignoreEvent为no时, 此时会检测时间响应*/
|
||||
if (self.needEventInterval > 0)
|
||||
{
|
||||
self.ignoreEvent = YES;
|
||||
[self performSelector:@selector(changeIgnoreEvent) withObject:@(NO) afterDelay:self.needEventInterval];
|
||||
}
|
||||
[self __uxy_sendAction:action to:target forEvent:event]; //此时交换走原来方法;
|
||||
}
|
||||
|
||||
- (void)changeIgnoreEvent
|
||||
{
|
||||
self.ignoreEvent = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
80
QXLive/Tools/Category/UIImage+QX.h
Normal file
80
QXLive/Tools/Category/UIImage+QX.h
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// UIImage+QX.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UIImage (QX)
|
||||
typedef NS_ENUM(NSUInteger, GradientType) {
|
||||
GradientTypeTopToBottom = 0,//从上到小
|
||||
GradientTypeLeftToRight = 1,//从左到右
|
||||
GradientTypeUpleftToLowright = 2,//左上到右下
|
||||
GradientTypeUprightToLowleft = 3,//右上到左下
|
||||
};
|
||||
|
||||
|
||||
// 指定尺寸 进行图片压缩
|
||||
- (NSData *)qx_compressImageQualityWithToByte:(NSInteger)maxLength;
|
||||
|
||||
|
||||
/**
|
||||
把view生成UIImage图片
|
||||
|
||||
@param view 传入view
|
||||
@return 生成的UIImage
|
||||
*/
|
||||
+ (UIImage *)qx_convertViewToImageWith:(UIView *)view;
|
||||
|
||||
+ (UIImage *)qx_gradientColorImageFromColors:(NSArray*)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize;
|
||||
|
||||
/**
|
||||
根据颜色,生成UIImage对象
|
||||
|
||||
@param color color对象
|
||||
@return UIImage 实例对象
|
||||
*/
|
||||
+ (instancetype)qx_imageWithColor:(UIColor *)color;
|
||||
|
||||
/**
|
||||
把图片image对象的size 缩放到控件的实际大小
|
||||
|
||||
@param size 控件实际尺寸
|
||||
@param fillColor 周围颜色
|
||||
@return 新的image对象
|
||||
*/
|
||||
- (instancetype)qx_imageFitWithSize:(CGSize)size fillColor:(UIColor *)fillColor;
|
||||
|
||||
/**
|
||||
把图片image对象,处理成圆角
|
||||
|
||||
@param radius 圆角半径
|
||||
@param corners 控制圆角的位置
|
||||
@param fillColor 被裁减掉区域的颜色
|
||||
@param zoomSize 缩放尺寸,应等于控件的尺寸
|
||||
@return 新的image对象
|
||||
*/
|
||||
- (instancetype)qx_imageCornerWithRadius:(CGFloat)radius byRoundingCorners:(UIRectCorner)corners fillColor:(UIColor *)fillColor zoomSize:(CGSize)zoomSize;
|
||||
|
||||
/**
|
||||
设置图片圆角
|
||||
|
||||
@param radius 圆角半径
|
||||
@param fillColor 被裁减掉区域的颜色
|
||||
@param zoomSize 缩放尺寸,应等于控件的尺寸
|
||||
@return 新的image对象
|
||||
*/
|
||||
- (instancetype)qx_imageCornerWithRadius:(CGFloat)radius fillColor:(UIColor *)fillColor zoomSize:(CGSize)zoomSize;
|
||||
|
||||
/*
|
||||
获取系统启动页的图片
|
||||
*/
|
||||
+ (instancetype)qx_getAppLauchImage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
139
QXLive/Tools/Category/UIImage+QX.m
Normal file
139
QXLive/Tools/Category/UIImage+QX.m
Normal file
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// UIImage+QX.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import "UIImage+QX.h"
|
||||
|
||||
@implementation UIImage (QX)
|
||||
// 指定尺寸 进行图片压缩
|
||||
- (NSData *)qx_compressImageQualityWithToByte:(NSInteger)maxLength {
|
||||
CGFloat compression = 1;
|
||||
NSData *data = UIImageJPEGRepresentation(self, compression);
|
||||
if (data.length <= maxLength) {
|
||||
return data;
|
||||
}
|
||||
compression = (CGFloat)maxLength / data.length;
|
||||
return UIImageJPEGRepresentation(self, compression);
|
||||
}
|
||||
|
||||
+ (UIImage *)qx_convertViewToImageWith:(UIView *)view {
|
||||
//UIGraphicsBeginImageContextWithOptions(区域大小, 是否是非透明的, 屏幕密度);
|
||||
UIGraphicsBeginImageContextWithOptions(view.frame.size, YES, [UIScreen mainScreen].scale);
|
||||
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
|
||||
UIImage *imageRet = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return imageRet;
|
||||
}
|
||||
|
||||
+ (UIImage *)qx_imageWithColor:(UIColor *)color {
|
||||
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
UIGraphicsBeginImageContext(rect.size);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
CGContextSetFillColorWithColor(context, [color CGColor]);
|
||||
CGContextFillRect(context, rect);
|
||||
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (UIImage *)qx_gradientColorImageFromColors:(NSArray *)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize {
|
||||
NSMutableArray *ar = [NSMutableArray array];
|
||||
for (UIColor *c in colors) {
|
||||
[ar addObject:(id)c.CGColor];
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(imgSize, YES, 1);
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextSaveGState(context);
|
||||
CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
|
||||
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
|
||||
CGPoint start;
|
||||
CGPoint end;
|
||||
|
||||
switch (gradientType) {
|
||||
case GradientTypeTopToBottom:
|
||||
start = CGPointMake(0.0, 0.0);
|
||||
end = CGPointMake(0.0, imgSize.height);
|
||||
break;
|
||||
case GradientTypeLeftToRight:
|
||||
start = CGPointMake(0.0, 0.0);
|
||||
end = CGPointMake(imgSize.width, 0.0);
|
||||
break;
|
||||
case GradientTypeUpleftToLowright:
|
||||
start = CGPointMake(0.0, 0.0);
|
||||
end = CGPointMake(imgSize.width, imgSize.height);
|
||||
break;
|
||||
case GradientTypeUprightToLowleft:
|
||||
start = CGPointMake(imgSize.width, 0.0);
|
||||
end = CGPointMake(0.0, imgSize.height);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
CGGradientRelease(gradient);
|
||||
CGContextRestoreGState(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (instancetype)qx_imageFitWithSize:(CGSize)size fillColor:(UIColor *)fillColor {
|
||||
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
|
||||
CGRect rect = (CGRect) { CGPointZero, size };
|
||||
[fillColor setFill];
|
||||
UIRectFill(rect);
|
||||
[self drawInRect:rect];
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (instancetype)qx_imageCornerWithRadius:(CGFloat)radius byRoundingCorners:(UIRectCorner)corners fillColor:(UIColor *)fillColor zoomSize:(CGSize)zoomSize {
|
||||
UIGraphicsBeginImageContextWithOptions(zoomSize, YES, 0);
|
||||
CGRect rect = (CGRect) { CGPointZero, zoomSize };
|
||||
[fillColor setFill];
|
||||
UIRectFill(rect);
|
||||
CGSize size = CGSizeMake(radius, radius);
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:size];
|
||||
[path addClip];
|
||||
[self drawInRect:rect];
|
||||
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
|
||||
return img;
|
||||
}
|
||||
|
||||
- (instancetype)qx_imageCornerWithRadius:(CGFloat)radius fillColor:(UIColor *)fillColor zoomSize:(CGSize)zoomSize {
|
||||
return [self qx_imageCornerWithRadius:radius byRoundingCorners:UIRectCornerAllCorners fillColor:fillColor zoomSize:zoomSize];
|
||||
}
|
||||
|
||||
+ (instancetype)qx_getAppLauchImage {
|
||||
CGSize viewSize = [UIScreen mainScreen].bounds.size;
|
||||
NSString *viewOrienttation = @"Portrait";
|
||||
NSString *launchImage = nil;
|
||||
NSArray *imageDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
|
||||
for (NSDictionary *dict in imageDict) {
|
||||
if (launchImage) {
|
||||
return [UIImage imageNamed:launchImage];
|
||||
}
|
||||
|
||||
CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
|
||||
if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrienttation isEqualToString:dict[@"UILaunchImageOrientation"]]) {
|
||||
launchImage = dict[@"UILaunchImageName"];
|
||||
}
|
||||
}
|
||||
if (!launchImage) {
|
||||
// 取不到就直接取,6s的
|
||||
return [UIImage imageNamed:@"LaunchImage-800-667h"];
|
||||
}
|
||||
return [UIImage imageNamed:launchImage];
|
||||
}
|
||||
|
||||
@end
|
||||
158
QXLive/Tools/Category/UIView+QX.h
Normal file
158
QXLive/Tools/Category/UIView+QX.h
Normal file
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// UIView+QX.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/27.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
typedef NS_ENUM(NSInteger , ShadowPathType) {
|
||||
ShadowPathTop = 1,
|
||||
ShadowPathBottom = 2,
|
||||
ShadowPathLeft = 3,
|
||||
ShadowPathRight = 4,
|
||||
ShadowPathCommon = 5,
|
||||
ShadowPathAround = 6,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, SRGradientDirection) {
|
||||
SRGradientDirectionTopToBottom, // 渐变色从上到下
|
||||
SRGradientDirectionLeftToRight, // 渐变色从左到右
|
||||
};
|
||||
@interface UIView (QX)
|
||||
/** 起点x坐标 */
|
||||
@property (nonatomic, assign) CGFloat x;
|
||||
/** 起点y坐标 */
|
||||
@property (nonatomic, assign) CGFloat y;
|
||||
/** 中心点x坐标 */
|
||||
@property (nonatomic, assign) CGFloat centerX;
|
||||
/** 中心点y坐标 */
|
||||
@property (nonatomic, assign) CGFloat centerY;
|
||||
/** 宽度 */
|
||||
@property (nonatomic, assign) CGFloat width;
|
||||
/** 高度 */
|
||||
@property (nonatomic, assign) CGFloat height;
|
||||
/** 顶部 */
|
||||
@property (nonatomic, assign) CGFloat top;
|
||||
/** 底部 */
|
||||
@property (nonatomic, assign) CGFloat bottom;
|
||||
/** 左边 */
|
||||
@property (nonatomic, assign) CGFloat left;
|
||||
/** 右边 */
|
||||
@property (nonatomic, assign) CGFloat right;
|
||||
/** size */
|
||||
@property (nonatomic, assign) CGSize size;
|
||||
/** origin */
|
||||
@property (nonatomic, assign) CGPoint origin;
|
||||
|
||||
/**
|
||||
快速给View添加4边阴影
|
||||
参数:阴影透明度,默认0
|
||||
*/
|
||||
- (void)addProjectionWithShadowOpacity:(CGFloat)shadowOpacity;
|
||||
/**
|
||||
快速给View添加4边阴影
|
||||
*/
|
||||
- (void)addShadowWithColor:(UIColor*)color radius:(CGFloat)radius;
|
||||
|
||||
- (void)addShadowWithColor:(UIColor*)color radius:(CGFloat)radius frame:(CGRect)frame;
|
||||
/**
|
||||
快速给View添加4边框
|
||||
参数:边框宽度
|
||||
*/
|
||||
- (void)addBorderWithWidth:(CGFloat)width;
|
||||
/**
|
||||
快速给View添加4边框
|
||||
width:边框宽度
|
||||
borderColor:边框颜色
|
||||
*/
|
||||
- (void)addBorderWithWidth:(CGFloat)width borderColor:(UIColor *)borderColor;
|
||||
/**
|
||||
快速给View添加圆角
|
||||
参数:圆角半径
|
||||
*/
|
||||
- (void)addRoundedCornersWithRadius:(CGFloat)radius;
|
||||
/**
|
||||
快速给View添加圆角
|
||||
radius:圆角半径
|
||||
corners:且那几个角
|
||||
类型共有以下几种:
|
||||
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
|
||||
UIRectCornerTopLeft,
|
||||
UIRectCornerTopRight ,
|
||||
UIRectCornerBottomLeft,
|
||||
UIRectCornerBottomRight,
|
||||
UIRectCornerAllCorners
|
||||
};
|
||||
使用案例:[self.mainView addRoundedCornersWithRadius:10 byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight]; // 切除了左下 右下
|
||||
*/
|
||||
- (void)addRoundedCornersWithRadius:(CGFloat)radius byRoundingCorners:(UIRectCorner)corners;
|
||||
|
||||
/************** 按自身Bounds计算 *************/
|
||||
|
||||
- (void)setTopToBottomGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors;
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors;
|
||||
|
||||
- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors roundedCorners:(CGFloat)radius;
|
||||
|
||||
- (void)setAroundRoundedCorners:(CGFloat)radius;
|
||||
|
||||
- (void)setTopRoundedCorners:(CGFloat)radius;
|
||||
|
||||
- (void)setBottomRoundedCorners:(CGFloat)radius;
|
||||
|
||||
- (void)setLeftRoundedCorners:(CGFloat)radius;
|
||||
|
||||
/************* 按Frame计算 *************/
|
||||
|
||||
/** 设置渐变色 */
|
||||
- (void)setViewBackgroundColorWithColorArray:(NSArray *)colorArray scaleArray:(NSArray * __nullable)scaleArray direction:(SRGradientDirection)direction;
|
||||
|
||||
/** 仅设置由上至下的渐变色 */
|
||||
- (void)setTopToBottomGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置由左至右的渐变色 */
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置自定义起始和结束位置的渐变色 */
|
||||
- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint frame:(CGRect)frame;
|
||||
|
||||
/** 设置由左至右的圆角渐变色 */
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *_Nullable)colors roundedCorners:(CGFloat)radius frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置裁剪 */
|
||||
- (void)setAroundRoundedCorners:(CGFloat)radius frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置顶部角裁剪 */
|
||||
- (void)setTopRoundedCorners:(CGFloat)radius frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置底部角裁剪 */
|
||||
- (void)setBottomRoundedCorners:(CGFloat)radius frame:(CGRect)frame;
|
||||
|
||||
/** 仅设置左侧角裁剪 */
|
||||
- (void)setLeftRoundedCorners:(CGFloat)radius frame:(CGRect)frame;
|
||||
|
||||
/**
|
||||
给UIView添加阴影
|
||||
@param shadowColor 阴影颜色
|
||||
@param shadowOpacity 阴影透明度 默认0
|
||||
@param shadowRadius 阴影半径 也就是阴影放射程度 默认3
|
||||
@param shadowPathType 阴影方向
|
||||
@param shadowPathWidth 阴影放射g宽度
|
||||
*/
|
||||
- (void)setViewShadowPathWithColor:(UIColor *)shadowColor shadowOpacity:(CGFloat)shadowOpacity shadowRadius:(CGFloat)shadowRadius shadowPathType:(ShadowPathType)shadowPathType shadowPathWidth:(CGFloat)shadowPathWidth;
|
||||
|
||||
- (void)addTapBlock:(void(^)(id obj))tapAction;
|
||||
|
||||
- (UIViewController *)viewController;
|
||||
|
||||
- (UINavigationController *)navigationController;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
508
QXLive/Tools/Category/UIView+QX.m
Normal file
508
QXLive/Tools/Category/UIView+QX.m
Normal file
@@ -0,0 +1,508 @@
|
||||
//
|
||||
// UIView+QX.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/27.
|
||||
//
|
||||
|
||||
#import "UIView+QX.h"
|
||||
static const void* tagValue = &tagValue;
|
||||
@implementation UIView (QX)
|
||||
- (void)setX:(CGFloat)x {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = x;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (void)setY:(CGFloat)y {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = y;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)x {
|
||||
return self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (CGFloat)y {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setCenterX:(CGFloat)centerX {
|
||||
|
||||
CGPoint center = self.center;
|
||||
center.x = centerX;
|
||||
self.center = center;
|
||||
|
||||
}
|
||||
|
||||
- (CGFloat)centerX {
|
||||
return self.center.x;
|
||||
}
|
||||
|
||||
- (void)setCenterY:(CGFloat)centerY{
|
||||
CGPoint center = self.center;
|
||||
center.y = centerY;
|
||||
self.center = center;
|
||||
}
|
||||
|
||||
- (CGFloat)centerY {
|
||||
return self.center.y;
|
||||
}
|
||||
|
||||
- (void)setWidth:(CGFloat)width {
|
||||
CGRect frame = self.frame;
|
||||
frame.size.width = width;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (void)setHeight:(CGFloat)height {
|
||||
CGRect frame = self.frame;
|
||||
frame.size.height = height;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)height {
|
||||
return self.frame.size.height;
|
||||
}
|
||||
|
||||
- (CGFloat)width {
|
||||
return self.frame.size.width;
|
||||
}
|
||||
|
||||
- (void)setSize:(CGSize)size {
|
||||
CGRect frame = self.frame;
|
||||
frame.size = size;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGSize)size {
|
||||
return self.frame.size;
|
||||
}
|
||||
|
||||
- (void)setOrigin:(CGPoint)origin {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin = origin;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGPoint)origin {
|
||||
return self.frame.origin;
|
||||
}
|
||||
- (CGFloat)top {
|
||||
return self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setTop:(CGFloat)top {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = top;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)left {
|
||||
return self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (void)setLeft:(CGFloat)left {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = left;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
|
||||
- (CGFloat)bottom {
|
||||
return self.frame.size.height + self.frame.origin.y;
|
||||
}
|
||||
|
||||
- (void)setBottom:(CGFloat)bottom {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.y = bottom - frame.size.height;
|
||||
self.frame = frame;
|
||||
}
|
||||
|
||||
- (CGFloat)right {
|
||||
return self.frame.size.width + self.frame.origin.x;
|
||||
}
|
||||
|
||||
- (void)setRight:(CGFloat)right {
|
||||
CGRect frame = self.frame;
|
||||
frame.origin.x = right - frame.size.width;
|
||||
self.frame = frame;
|
||||
}
|
||||
#pragma mark - 快速添加阴影
|
||||
- (void)addProjectionWithShadowOpacity:(CGFloat)shadowOpacity {
|
||||
self.layer.shadowColor = [UIColor blackColor].CGColor;//shadowColor阴影颜色
|
||||
self.layer.shadowOffset = CGSizeMake(0,0);//shadowOffset阴影偏移,x向右偏移2,y向下偏移6,默认(0, -3),这个跟shadowRadius配合使用
|
||||
self.layer.shadowOpacity = shadowOpacity;//阴影透明度,默认0
|
||||
self.layer.shadowRadius = 3;//阴影半径,默认3
|
||||
}
|
||||
- (void)addShadowWithColor:(UIColor*)color radius:(CGFloat)radius{
|
||||
CALayer *subLayer = [CALayer layer];
|
||||
subLayer.frame = self.frame;
|
||||
subLayer.cornerRadius = radius;
|
||||
subLayer.masksToBounds = NO;
|
||||
subLayer.backgroundColor = color.CGColor;
|
||||
subLayer.shadowColor = color.CGColor;//shadowColor阴影颜色
|
||||
subLayer.shadowOffset = CGSizeMake(0,2);
|
||||
subLayer.shadowOpacity = 0.5;//阴影透明度,默认0
|
||||
subLayer.shadowRadius = radius;//阴影半径,默认3
|
||||
[self.layer insertSublayer:subLayer below:self.layer];
|
||||
}
|
||||
- (void)addShadowWithColor:(UIColor*)color radius:(CGFloat)radius frame:(CGRect)frame{
|
||||
CALayer *subLayer = [CALayer layer];
|
||||
subLayer.frame = frame;
|
||||
subLayer.cornerRadius = radius;
|
||||
subLayer.masksToBounds = YES;
|
||||
subLayer.backgroundColor = color.CGColor;
|
||||
subLayer.shadowColor = color.CGColor;//shadowColor阴影颜色
|
||||
subLayer.shadowOffset = CGSizeMake(0,-3);
|
||||
subLayer.shadowOpacity = 0.5;//阴影透明度,默认0
|
||||
subLayer.shadowRadius = radius;//阴影半径,默认3
|
||||
[self.layer insertSublayer:subLayer below:self.layer];
|
||||
}
|
||||
- (void)addBorderWithWidth:(CGFloat)width {
|
||||
self.layer.borderWidth = width;
|
||||
self.layer.borderColor = [UIColor blackColor].CGColor;
|
||||
}
|
||||
- (void)addBorderWithWidth:(CGFloat)width borderColor:(UIColor *)borderColor {
|
||||
self.layer.borderWidth = width;
|
||||
self.layer.borderColor = borderColor.CGColor;
|
||||
}
|
||||
- (void)addRoundedCornersWithRadius:(CGFloat)radius {
|
||||
self.layer.cornerRadius = radius;
|
||||
self.layer.masksToBounds = YES;
|
||||
}
|
||||
- (void)addRoundedCornersWithRadius:(CGFloat)radius byRoundingCorners:(UIRectCorner)corners{
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
|
||||
maskLayer.frame = self.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setTopToBottomGradientBackgroundWithColors:(NSArray<UIColor *> *)colors {
|
||||
|
||||
[self setGradientBackgroundWithColors:colors startPoint:CGPointMake(0.5, 0) endPoint:CGPointMake(0.5, 1)];
|
||||
}
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *)colors {
|
||||
|
||||
[self setGradientBackgroundWithColors:colors startPoint:CGPointMake(0, 0.5) endPoint:CGPointMake(1, 0.5)];
|
||||
}
|
||||
|
||||
- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *)colors startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
|
||||
|
||||
NSMutableArray *colorsM = [NSMutableArray array];
|
||||
for (UIColor *color in colors) {
|
||||
[colorsM addObject:(__bridge id)color.CGColor];
|
||||
}
|
||||
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = colorsM;
|
||||
gradientLayer.locations = @[@0, @1];
|
||||
gradientLayer.startPoint = startPoint;
|
||||
gradientLayer.endPoint = endPoint;
|
||||
gradientLayer.frame = self.bounds;
|
||||
// [self.layer addSublayer:gradientLayer];
|
||||
|
||||
//layout中重复添加CAGradientLayer,需要清空多余的渐变层
|
||||
NSArray<CALayer *> *subLayers = self.layer.sublayers;
|
||||
NSArray<CALayer *> *removedLayers = [subLayers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return [evaluatedObject isKindOfClass:[CAGradientLayer class]];
|
||||
}]];
|
||||
[removedLayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[obj removeFromSuperlayer];
|
||||
}];
|
||||
|
||||
[self.layer insertSublayer:gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *)colors roundedCorners:(CGFloat)radius {
|
||||
|
||||
NSMutableArray *colorsM = [NSMutableArray array];
|
||||
for (UIColor *color in colors) {
|
||||
[colorsM addObject:(__bridge id)color.CGColor];
|
||||
}
|
||||
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = colorsM;
|
||||
gradientLayer.locations = @[@0, @1];
|
||||
gradientLayer.startPoint = CGPointMake(0, 0.5);
|
||||
gradientLayer.endPoint = CGPointMake(1, 0.5);
|
||||
gradientLayer.frame = self.bounds;
|
||||
gradientLayer.cornerRadius = radius;
|
||||
gradientLayer.masksToBounds = YES;
|
||||
|
||||
//layout中重复添加CAGradientLayer,需要清空多余的渐变层
|
||||
NSArray<CALayer *> *subLayers = self.layer.sublayers;
|
||||
NSArray<CALayer *> *removedLayers = [subLayers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return [evaluatedObject isKindOfClass:[CAGradientLayer class]];
|
||||
}]];
|
||||
[removedLayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[obj removeFromSuperlayer];
|
||||
}];
|
||||
|
||||
[self.layer insertSublayer:gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)setAroundRoundedCorners:(CGFloat)radius {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = self.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setTopRoundedCorners:(CGFloat)radius {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = self.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setBottomRoundedCorners:(CGFloat)radius {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = self.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setLeftRoundedCorners:(CGFloat)radius {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = self.bounds;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
#pragma mark - 按Frame计算
|
||||
|
||||
- (void)setTopToBottomGradientBackgroundWithColors:(NSArray<UIColor *> *)colors frame:(CGRect)frame {
|
||||
|
||||
[self setGradientBackgroundWithColors:colors startPoint:CGPointMake(0.5, 0) endPoint:CGPointMake(0.5, 1) frame:frame];
|
||||
}
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *)colors frame:(CGRect)frame {
|
||||
|
||||
[self setGradientBackgroundWithColors:colors startPoint:CGPointMake(0, 0.5) endPoint:CGPointMake(1, 0.5) frame:frame];
|
||||
}
|
||||
|
||||
- (void)setGradientBackgroundWithColors:(NSArray<UIColor *> *)colors startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint frame:(CGRect)frame {
|
||||
|
||||
NSMutableArray *colorsM = [NSMutableArray array];
|
||||
for (UIColor *color in colors) {
|
||||
[colorsM addObject:(__bridge id)color.CGColor];
|
||||
}
|
||||
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = colorsM;
|
||||
gradientLayer.locations = @[@0, @1];
|
||||
gradientLayer.startPoint = startPoint;
|
||||
gradientLayer.endPoint = endPoint;
|
||||
gradientLayer.frame = frame;
|
||||
// [self.layer addSublayer:gradientLayer];
|
||||
|
||||
//layout中重复添加CAGradientLayer,需要清空多余的渐变层
|
||||
NSArray<CALayer *> *subLayers = self.layer.sublayers;
|
||||
NSArray<CALayer *> *removedLayers = [subLayers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return [evaluatedObject isKindOfClass:[CAGradientLayer class]];
|
||||
}]];
|
||||
[removedLayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[obj removeFromSuperlayer];
|
||||
}];
|
||||
|
||||
[self.layer insertSublayer:gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)setLeftToRightGradientBackgroundWithColors:(NSArray<UIColor *> *)colors roundedCorners:(CGFloat)radius frame:(CGRect)frame {
|
||||
|
||||
NSMutableArray *colorsM = [NSMutableArray array];
|
||||
for (UIColor *color in colors) {
|
||||
[colorsM addObject:(__bridge id)color.CGColor];
|
||||
}
|
||||
|
||||
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
|
||||
gradientLayer.colors = colorsM;
|
||||
gradientLayer.locations = @[@0, @1];
|
||||
gradientLayer.startPoint = CGPointMake(0, 0.5);
|
||||
gradientLayer.endPoint = CGPointMake(1, 0.5);
|
||||
gradientLayer.frame = frame;
|
||||
gradientLayer.cornerRadius = radius;
|
||||
gradientLayer.masksToBounds = YES;
|
||||
|
||||
//layout中重复添加CAGradientLayer,需要清空多余的渐变层
|
||||
NSArray<CALayer *> *subLayers = self.layer.sublayers;
|
||||
NSArray<CALayer *> *removedLayers = [subLayers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||
return [evaluatedObject isKindOfClass:[CAGradientLayer class]];
|
||||
}]];
|
||||
[removedLayers enumerateObjectsUsingBlock:^(CALayer * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[obj removeFromSuperlayer];
|
||||
}];
|
||||
|
||||
[self.layer insertSublayer:gradientLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)setAroundRoundedCorners:(CGFloat)radius frame:(CGRect)frame {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = frame;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setTopRoundedCorners:(CGFloat)radius frame:(CGRect)frame {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = frame;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setBottomRoundedCorners:(CGFloat)radius frame:(CGRect)frame {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = frame;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setLeftRoundedCorners:(CGFloat)radius frame:(CGRect)frame {
|
||||
|
||||
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomLeft cornerRadii:CGSizeMake(radius, radius)];
|
||||
|
||||
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
|
||||
maskLayer.frame = frame;
|
||||
maskLayer.path = maskPath.CGPath;
|
||||
self.layer.mask = maskLayer;
|
||||
}
|
||||
|
||||
- (void)setViewShadowPathWithColor:(UIColor *)shadowColor shadowOpacity:(CGFloat)shadowOpacity shadowRadius:(CGFloat)shadowRadius shadowPathType:(ShadowPathType)shadowPathType shadowPathWidth:(CGFloat)shadowPathWidth {
|
||||
|
||||
self.layer.masksToBounds = NO;//必须要等于NO否则会把阴影切割隐藏掉
|
||||
self.layer.shadowColor = shadowColor.CGColor;// 阴影颜色
|
||||
self.layer.shadowOpacity = shadowOpacity;// 阴影透明度,默认0
|
||||
self.layer.shadowOffset = CGSizeZero;//shadowOffset阴影偏移,默认(0, -3),这个跟shadowRadius配合使用
|
||||
self.layer.shadowRadius = shadowRadius;//阴影半径,默认3
|
||||
CGRect shadowRect = CGRectZero;
|
||||
CGFloat originX,originY,sizeWith,sizeHeight;
|
||||
originX = 0;
|
||||
originY = 0;
|
||||
sizeWith = self.bounds.size.width;
|
||||
sizeHeight = self.bounds.size.height;
|
||||
|
||||
if (shadowPathType == ShadowPathTop) {
|
||||
shadowRect = CGRectMake(originX, originY-shadowPathWidth/2, sizeWith, shadowPathWidth);
|
||||
}else if (shadowPathType == ShadowPathBottom){
|
||||
shadowRect = CGRectMake(originY, sizeHeight-shadowPathWidth/2, sizeWith, shadowPathWidth);
|
||||
}else if (shadowPathType == ShadowPathLeft){
|
||||
shadowRect = CGRectMake(originX-shadowPathWidth/2, originY, shadowPathWidth, sizeHeight);
|
||||
}else if (shadowPathType == ShadowPathRight){
|
||||
shadowRect = CGRectMake(sizeWith-shadowPathWidth/2, originY, shadowPathWidth, sizeHeight);
|
||||
}else if (shadowPathType == ShadowPathCommon){
|
||||
shadowRect = CGRectMake(originX-shadowPathWidth/2, 2, sizeWith+shadowPathWidth, sizeHeight+shadowPathWidth/2);
|
||||
}else if (shadowPathType == ShadowPathAround){
|
||||
shadowRect = CGRectMake(originX-shadowPathWidth/2, originY-shadowPathWidth/2, sizeWith+shadowPathWidth, sizeHeight+shadowPathWidth);
|
||||
}
|
||||
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:shadowRect];
|
||||
self.layer.shadowPath = bezierPath.CGPath;//阴影路径
|
||||
}
|
||||
|
||||
|
||||
- (void)setViewBackgroundColorWithColorArray:(NSArray *)colorArray scaleArray:(NSArray * __nullable)scaleArray direction:(SRGradientDirection)direction {
|
||||
/*
|
||||
layoutIfNeeded不一定会调用layoutSubviews方法。
|
||||
setNeedsLayout一定会调用layoutSubviews方法(有延迟,在下一轮runloop结束前)。
|
||||
如果想在当前runloop中立即刷新,调用顺序应该是:
|
||||
[self setNeedsLayout];
|
||||
[self layoutIfNeeded];
|
||||
*/
|
||||
[self setNeedsLayout];
|
||||
[self layoutIfNeeded];
|
||||
|
||||
CAGradientLayer *gradinentlayer = [CAGradientLayer layer];
|
||||
if (direction == SRGradientDirectionLeftToRight) {
|
||||
gradinentlayer.startPoint = CGPointMake(0, 0);
|
||||
gradinentlayer.endPoint = CGPointMake(1.0, 0);
|
||||
}else {
|
||||
gradinentlayer.startPoint = CGPointMake(0, 0);
|
||||
gradinentlayer.endPoint = CGPointMake(0, 1.0);
|
||||
}
|
||||
|
||||
NSMutableArray *CGColorArray = [NSMutableArray new];
|
||||
for (UIColor *color in colorArray) {
|
||||
[CGColorArray addObject:(__bridge id)color.CGColor];
|
||||
}
|
||||
if (!scaleArray) {
|
||||
|
||||
}else {
|
||||
gradinentlayer.locations = scaleArray;
|
||||
}
|
||||
gradinentlayer.colors = CGColorArray;
|
||||
gradinentlayer.frame = self.bounds;
|
||||
[self.layer addSublayer:gradinentlayer];
|
||||
|
||||
}
|
||||
- (void)tap{
|
||||
if (self.tapAction) {
|
||||
self.tapAction(self);
|
||||
}
|
||||
}
|
||||
- (void)addTapBlock:(void(^)(id obj))tapAction{
|
||||
self.tapAction = tapAction;
|
||||
if (![self gestureRecognizers]) {
|
||||
self.userInteractionEnabled = YES;
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
|
||||
[self addGestureRecognizer:tap];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setTapAction:(void (^)(id))tapAction {
|
||||
objc_setAssociatedObject(self, tagValue, tapAction, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
-(void (^)(id))tapAction {
|
||||
return objc_getAssociatedObject(self, tagValue);
|
||||
}
|
||||
|
||||
- (UIViewController *)viewController {
|
||||
|
||||
id nextResponder = [self nextResponder];
|
||||
while (nextResponder != nil) {
|
||||
if ([nextResponder isKindOfClass:[UIViewController class]]) {
|
||||
UIViewController *vc = (UIViewController *)nextResponder;
|
||||
return vc;
|
||||
}
|
||||
nextResponder = [nextResponder nextResponder];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UINavigationController *)navigationController {
|
||||
|
||||
id nextResponder = [self nextResponder];
|
||||
while (nextResponder != nil) {
|
||||
if ([nextResponder isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController *vc = (UINavigationController *)nextResponder;
|
||||
return vc;
|
||||
}
|
||||
nextResponder = [nextResponder nextResponder];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
500
QXLive/Tools/MarqueeLabel.h
Executable file
500
QXLive/Tools/MarqueeLabel.h
Executable file
@@ -0,0 +1,500 @@
|
||||
|
||||
//
|
||||
// MarqueeLabel.h
|
||||
//
|
||||
// Created by Charles Powell on 1/31/11.
|
||||
// Copyright (c) 2011-2015 Charles Powell. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/** An enum that defines the types of `MarqueeLabel` scrolling */
|
||||
typedef NS_ENUM(NSUInteger, MarqueeType) {
|
||||
/** Scrolls left first, then back right to the original position. */
|
||||
MLLeftRight = 0,
|
||||
/** Scrolls right first, then back left to the original position. */
|
||||
MLRightLeft = 1,
|
||||
/** Continuously scrolls left (with a pause at the original position if animationDelay is set). See the `trailingBuffer` property to define a spacing between the repeating strings.*/
|
||||
MLContinuous = 2,
|
||||
/** Continuously scrolls right (with a pause at the original position if animationDelay is set). See the `trailingBuffer` property to define a spacing between the repeating strings.*/
|
||||
MLContinuousReverse = 3,
|
||||
/** Scrolls left first, then does not return to the original position. */
|
||||
MLLeft = 4,
|
||||
/** Scrolls right first, then does not return to the original position. */
|
||||
MLRight = 5
|
||||
};
|
||||
|
||||
|
||||
#ifndef IBInspectable
|
||||
#define IBInspectable
|
||||
#endif
|
||||
|
||||
/**
|
||||
MarqueeLabel is a UILabel subclass adds a scrolling marquee effect when the text of a label instance outgrows the available width. Instances of `MarqueeLabel` can be configured
|
||||
for label scrolling direction/looping, speed/rate, and other options.
|
||||
*/
|
||||
|
||||
IB_DESIGNABLE
|
||||
@interface MarqueeLabel : UILabel <CAAnimationDelegate>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Creating MarqueeLabels
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Returns a newly initialized `MarqueeLabel` instance.
|
||||
|
||||
The default scroll duration of 7.0 seconds and fade length of 0.0 are used.
|
||||
|
||||
@param frame A rectangle specifying the initial location and size of the view in its superview's coordinates. Text (for the given font, font size, etc.) that does not fit in this frame will automatically scroll.
|
||||
@return An initialized `MarqueeLabel` object or nil if the object couldn't be created.
|
||||
*/
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
|
||||
|
||||
/** Returns a newly initialized `MarqueeLabel` instance with the specified scroll rate and edge transparency fade length.
|
||||
|
||||
You must specify a non-zero rate, and you cannot thereafter modify the rate.
|
||||
|
||||
@param frame A rectangle specifying the initial location and size of the view in its superview's coordinates. Text (for the given font, font size, etc.) that does not fit in this frame will automatically scroll.
|
||||
@param pixelsPerSec A rate of scroll for the label scroll animation. Must be non-zero. Note that this will be the maximum rate for ease-type animation.
|
||||
@param fadeLength A length of transparency fade at the left and right edges of the `MarqueeLabel` instance's frame.
|
||||
@see fadeLength
|
||||
@return An initialized `MarqueeLabel` object or nil if the object couldn't be created.
|
||||
*/
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame rate:(CGFloat)pixelsPerSec andFadeLength:(CGFloat)fadeLength;
|
||||
|
||||
|
||||
/** Returns a newly initialized `MarqueeLabel` instance with the specified scroll duration and edge transparency fade length.
|
||||
|
||||
You must specify a non-zero duration, and you cannot thereafter modify the duration.
|
||||
|
||||
@param frame A rectangle specifying the initial location and size of the view in its superview's coordinates. Text (for the given font, font size, etc.) that does not fit in this frame will automatically scroll.
|
||||
@param scrollDuration A scroll duration the label scroll animation. Must be non-zero. This will be the duration that the animation takes for one-half of the scroll cycle in the case of left-right and right-left marquee types, and for one loop of a continuous marquee type.
|
||||
@param fadeLength A length of transparency fade at the left and right edges of the `MarqueeLabel` instance's frame.
|
||||
@see fadeLength
|
||||
@return An initialized `MarqueeLabel` object or nil if the object couldn't be created.
|
||||
*/
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame duration:(NSTimeInterval)scrollDuration andFadeLength:(CGFloat)fadeLength;
|
||||
|
||||
|
||||
/** Resizes the view to the minimum size necessary to fully enclose the current text (i.e. without scrolling), up to the maximum size specified.
|
||||
|
||||
The current origin of the frame is retained.
|
||||
|
||||
@param maxSize The maximum size up to which the view should be resized. Passing `CGSizeZero` will result in no maximum size limit.
|
||||
@param adjustHeight A boolean that can be used to indicate if the view's height should also be adjusted. Note that this has no impact on scrolling.
|
||||
*/
|
||||
- (void)minimizeLabelFrameWithMaximumSize:(CGSize)maxSize adjustHeight:(BOOL)adjustHeight;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Configuration Options
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Specifies the animation curve used in the scrolling motion of the labels.
|
||||
|
||||
Allowable options:
|
||||
|
||||
- `UIViewAnimationOptionCurveEaseInOut`
|
||||
- `UIViewAnimationOptionCurveEaseIn`
|
||||
- `UIViewAnimationOptionCurveEaseOut`
|
||||
- `UIViewAnimationOptionCurveLinear`
|
||||
|
||||
Defaults to `UIViewAnimationOptionCurveEaseInOut`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) UIViewAnimationOptions animationCurve;
|
||||
|
||||
|
||||
/** A boolean property that sets whether the `MarqueeLabel` should behave like a normal UILabel.
|
||||
|
||||
When set to `YES` the `MarqueeLabel` will behave like a normal UILabel, and will not begin scrolling when the text is
|
||||
larger than the specified frame. The change takes effect immediately, removing any in-flight animation as well as any
|
||||
current edge fade. Note that the `MarqueeLabel` will respect the current values of the `lineBreakMode` and `textAlignment`
|
||||
properties while labelized.
|
||||
|
||||
To simply prevent automatic scrolling, use the `holdScrolling` property.
|
||||
|
||||
Defaults to `NO`.
|
||||
|
||||
@see holdScrolling
|
||||
@see lineBreakMode
|
||||
@warning The label will not automatically scroll when this property is set to `YES`.
|
||||
@warning The UILabel default setting for the `lineBreakMode` property is `NSLineBreakByTruncatingTail`, which truncates
|
||||
the text adds an ellipsis glyph (...). Set the `lineBreakMode` property to `NSLineBreakByClipping` in order to avoid the
|
||||
ellipsis, especially if using an edge transparency fade.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable BOOL labelize;
|
||||
|
||||
|
||||
/** A boolean property that sets whether the `MarqueeLabel` should hold (prevent) label scrolling.
|
||||
|
||||
When set to `YES`, the `MarqueeLabel` will not automatically scroll even its text is larger than the specified frame,
|
||||
although the specified edge fades will remain.
|
||||
|
||||
To set the `MarqueeLabel` to act like a normal UILabel, use the `labelize` property.
|
||||
|
||||
Defaults to `NO`.
|
||||
|
||||
@see labelize
|
||||
@warning The label will not automatically scroll when this property is set to `YES`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable BOOL holdScrolling;
|
||||
|
||||
|
||||
/** A boolean property that sets whether the `MarqueeLabel` should only begin a scroll when tapped.
|
||||
|
||||
If this property is set to `YES`, the `MarqueeLabel` will begin a scroll animation cycle only when tapped. The label will
|
||||
not automatically being a scroll. This setting overrides the setting of the `holdScrolling` property.
|
||||
|
||||
Defaults to `NO` .
|
||||
|
||||
@warning The label will not automatically scroll when this property is set to `YES`.
|
||||
@see holdScrolling
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable BOOL tapToScroll;
|
||||
|
||||
|
||||
/** Defines the direction and method in which the `MarqueeLabel` instance scrolls.
|
||||
|
||||
`MarqueeLabel` supports four types of scrolling: `MLLeftRight`, `MLRightLeft`, `MLContinuous`, and `MLContinuousReverse`.
|
||||
|
||||
Given the nature of how text direction works, the options for the `marqueeType` property require specific text alignments
|
||||
and will set the textAlignment property accordingly.
|
||||
|
||||
- `MLLeftRight` type is ONLY compatible with a label text alignment of `NSTextAlignmentLeft`.
|
||||
- `MLRightLeft` type is ONLY compatible with a label text alignment of `NSTextAlignmentRight`.
|
||||
- `MLContinuous` does not require a text alignment (it is effectively centered).
|
||||
- `MLContinuousReverse` does not require a text alignment (it is effectively centered).
|
||||
|
||||
Defaults to `MLLeftRight`.
|
||||
|
||||
@see MarqueeType
|
||||
@see textAlignment
|
||||
*/
|
||||
|
||||
#if TARGET_INTERFACE_BUILDER
|
||||
@property (nonatomic, assign) IBInspectable NSInteger marqueeType;
|
||||
#else
|
||||
@property (nonatomic, assign) MarqueeType marqueeType;
|
||||
#endif
|
||||
|
||||
/** Defines the duration of the scrolling animation.
|
||||
|
||||
This property sets the amount of time it will take for the scrolling animation to complete a
|
||||
scrolling cycle. Note that for `MLLeftRight` and `MLRightLeft`, a cycle consists of the animation away,
|
||||
a pause (if a delay is specified), and the animation back to the original position.
|
||||
|
||||
Setting this property will automatically override any value previously set to the `rate` property, and the `rate`
|
||||
property will be set to `0.0`.
|
||||
|
||||
@see rate
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat scrollDuration;
|
||||
|
||||
|
||||
/** Defines the rate at which the label will scroll, in pixels per second.
|
||||
|
||||
Setting this property will automatically override any value previousy set to the `scrollDuration` property, and the
|
||||
`scrollDuration` property will be set to `0.0`. Note that this is the rate at which the label would scroll if it
|
||||
moved at a constant speed - with other animation curves the rate will be slightly different.
|
||||
|
||||
@see scrollDuration
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat rate;
|
||||
|
||||
|
||||
/** A buffer (offset) between the leading edge of the label text and the label frame.
|
||||
|
||||
This property adds additional space between the leading edge of the label text and the label frame. The
|
||||
leading edge is the edge of the label text facing the direction of scroll (i.e. the edge that animates
|
||||
offscreen first during scrolling).
|
||||
|
||||
Defaults to `0`.
|
||||
|
||||
@note The value set to this property affects label positioning at all times (including when `labelize` is set to `YES`),
|
||||
including when the text string length is short enough that the label does not need to scroll.
|
||||
|
||||
@note For `MLContinuous`-type labels, the smallest value of `leadingBuffer`, 'trailingBuffer`, and `fadeLength`
|
||||
is used as spacing between the two label instances. Zero is an allowable value for all three properties.
|
||||
|
||||
@see trailingBuffer
|
||||
@since Available in 2.1.0 and later.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat leadingBuffer;
|
||||
|
||||
|
||||
/** A buffer (offset) between the trailing edge of the label text and the label frame.
|
||||
|
||||
This property adds additional space (buffer) between the trailing edge of the label text and the label frame. The
|
||||
trailing edge is the edge of the label text facing away from the direction of scroll (i.e. the edge that animates
|
||||
offscreen last during scrolling).
|
||||
|
||||
Defaults to `0`.
|
||||
|
||||
@note For `MLContinuous`-type labels, the smallest value of `leadingBuffer`, 'trailingBuffer`, and `fadeLength`
|
||||
is used as spacing between the two label instances. Zero is an allowable value for all three properties.
|
||||
|
||||
@note The value set to this property has no effect when the `labelize` property is set to `YES`.
|
||||
|
||||
@see leadingBuffer
|
||||
@since Available in 2.1.0 and later.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat trailingBuffer;
|
||||
|
||||
|
||||
/**
|
||||
@deprecated Use `trailingBuffer` instead. Values set to this property are simply forwarded to `trailingBuffer`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) CGFloat continuousMarqueeExtraBuffer __deprecated_msg("Use trailingBuffer instead.");
|
||||
|
||||
|
||||
/** The length of transparency fade at the left and right edges of the `MarqueeLabel` instance's frame.
|
||||
|
||||
This propery sets the size (in points) of the view edge transparency fades on the left and right edges of a `MarqueeLabel`. The
|
||||
transparency fades from an alpha of 1.0 (fully visible) to 0.0 (fully transparent) over this distance. Values set to this property
|
||||
will be sanitized to prevent a fade length greater than 1/2 of the frame width.
|
||||
|
||||
Defaults to `0`.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat fadeLength;
|
||||
|
||||
|
||||
/** The length of delay in seconds that the label pauses at the completion of a scroll. */
|
||||
|
||||
@property (nonatomic, assign) IBInspectable CGFloat animationDelay;
|
||||
|
||||
|
||||
/** The read-only duration of the scroll animation (not including delay).
|
||||
|
||||
The value of this property is calculated when using the `scrollRate` property to set label animation speed. The value of this property
|
||||
is equal to the value of `scrollDuration` property when using the `scrollDuration` property to set label animation speed.
|
||||
|
||||
@see scrollDuration
|
||||
@see scrollRate
|
||||
*/
|
||||
|
||||
@property (nonatomic, readonly) NSTimeInterval animationDuration;
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Animation control
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Immediately resets the label to the home position, cancelling any in-flight scroll animation, and restarts the scroll animation if the appropriate conditions are met.
|
||||
|
||||
@see triggerScrollStart
|
||||
*/
|
||||
|
||||
- (void)restartLabel;
|
||||
|
||||
|
||||
/** Immediately resets the label to the home position, cancelling any in-flight scroll animation.
|
||||
|
||||
The text is immediately returned to the home position. Scrolling will not resume automatically after a call to this method.
|
||||
To re-initiate scrolling use a call to `restartLabel` or `triggerScrollStart`, or make a change to a UILabel property such as text, bounds/frame,
|
||||
font, font size, etc.
|
||||
|
||||
@see restartLabel
|
||||
@see triggerScrollStart
|
||||
@since Available in 2.4.0 and later.
|
||||
*/
|
||||
|
||||
- (void)shutdownLabel;
|
||||
|
||||
|
||||
/**
|
||||
@deprecated Use `resetLabel:` instead.
|
||||
*/
|
||||
|
||||
- (void)resetLabel __deprecated_msg("Use resetLabel instead");
|
||||
|
||||
|
||||
/** Pauses the text scrolling animation, at any point during an in-progress animation.
|
||||
|
||||
@note This method has no effect if a scroll animation is NOT already in progress. To prevent automatic scrolling on a newly-initialized label prior to its presentation onscreen, see the `holdScrolling` property.
|
||||
|
||||
@see holdScrolling
|
||||
@see unpauseLabel
|
||||
*/
|
||||
|
||||
- (void)pauseLabel;
|
||||
|
||||
|
||||
/** Un-pauses a previously paused text scrolling animation. This method has no effect if the label was not previously paused using `pauseLabel`.
|
||||
|
||||
@see pauseLabel
|
||||
*/
|
||||
|
||||
- (void)unpauseLabel;
|
||||
|
||||
|
||||
/** Overrides any non-size condition which is preventing the receiver from automatically scrolling, and begins a scroll animation.
|
||||
|
||||
Currently the only non-size conditions which can prevent a label from scrolling are the `tapToScroll` and `holdScrolling` properties. This
|
||||
method will not force a label with a string that fits inside the label bounds (i.e. that would not automatically scroll) to begin a scroll
|
||||
animation.
|
||||
|
||||
Upon the completion of the first forced scroll animation, the receiver will not automatically continue to scroll unless the conditions
|
||||
preventing scrolling have been removed.
|
||||
|
||||
@note This method has no effect if called during an already in-flight scroll animation.
|
||||
|
||||
@see restartLabel
|
||||
@since Available in 2.2.0 and later.
|
||||
*/
|
||||
|
||||
- (void)triggerScrollStart;
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Animation Status
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Called when the label animation is about to begin.
|
||||
|
||||
The default implementation of this method does nothing. Subclasses may override this method in order to perform any custom actions just as
|
||||
the label animation begins. This is only called in the event that the conditions for scrolling to begin are met.
|
||||
|
||||
@since Available in 1.5.0 and later.
|
||||
*/
|
||||
|
||||
- (void)labelWillBeginScroll;
|
||||
|
||||
|
||||
/** Called when the label animation has finished, and the label is at the home position.
|
||||
|
||||
The default implementation of this method does nothing. Subclasses may override this method in order to perform any custom actions jas as
|
||||
the label animation completes, and before the next animation would begin (assuming the scroll conditions are met).
|
||||
|
||||
@param finished A Boolean that indicates whether or not the scroll animation actually finished before the completion handler was called.
|
||||
@since Available in 1.5.0 and later.
|
||||
|
||||
@warning This method will be called, and the `finished` parameter will be `NO`, when any property changes are made that would cause the label
|
||||
scrolling to be automatically reset. This includes changes to label text and font/font size changes.
|
||||
*/
|
||||
|
||||
- (void)labelReturnedToHome:(BOOL)finished;
|
||||
|
||||
|
||||
/** A boolean property that indicates if the label's scroll animation has been paused.
|
||||
|
||||
@see pauseLabel
|
||||
@see unpauseLabel
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL isPaused;
|
||||
|
||||
|
||||
/** A boolean property that indicates if the label is currently away from the home location.
|
||||
|
||||
The "home" location is the traditional location of `UILabel` text. This property essentially reflects if a scroll animation is underway.
|
||||
*/
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL awayFromHome;
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @name Bulk-manipulation Methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** Convenience method to restart all `MarqueeLabel` instances that have the specified view controller in their next responder chain.
|
||||
|
||||
This method sends a `NSNotification` to all `MarqueeLabel` instances with the specified view controller in their next responder chain.
|
||||
The scrolling animation of these instances will be automatically restarted. This is equivalent to calling `restartLabel` on all affected
|
||||
instances.
|
||||
|
||||
There is currently no functional difference between this method and `controllerViewDidAppear:` or `controllerViewWillAppear:`. The methods may
|
||||
be used interchangeably.
|
||||
|
||||
@warning View controllers that appear with animation (such as from underneath a modal-style controller) can cause some `MarqueeLabel` text
|
||||
position "jumping" when this method is used in `viewDidAppear` if scroll animations are already underway. Use this method inside `viewWillAppear:`
|
||||
instead to avoid this problem.
|
||||
|
||||
@warning This method may not function properly if passed the parent view controller when using view controller containment.
|
||||
|
||||
@param controller The view controller that has appeared.
|
||||
@see restartLabel
|
||||
@see controllerViewDidAppear:
|
||||
@see controllerViewWillAppear:
|
||||
@since Available in 1.3.1 and later.
|
||||
*/
|
||||
|
||||
+ (void)restartLabelsOfController:(UIViewController *)controller;
|
||||
|
||||
|
||||
/** Convenience method to restart all `MarqueeLabel` instances that have the specified view controller in their next responder chain.
|
||||
|
||||
Alternative to `restartLabelsOfController:`. This method is retained for backwards compatibility and future enhancements.
|
||||
|
||||
@param controller The view controller that has appeared.
|
||||
@see restartLabel
|
||||
@see controllerViewWillAppear:
|
||||
@since Available in 1.2.7 and later.
|
||||
*/
|
||||
|
||||
+ (void)controllerViewDidAppear:(UIViewController *)controller;
|
||||
|
||||
|
||||
/** Convenience method to restart all `MarqueeLabel` instances that have the specified view controller in their next responder chain.
|
||||
|
||||
Alternative to `restartLabelsOfController:`. This method is retained for backwards compatibility and future enhancements.
|
||||
|
||||
@param controller The view controller that has appeared.
|
||||
@see restartLabel
|
||||
@see controllerViewDidAppear:
|
||||
@since Available in 1.2.8 and later.
|
||||
*/
|
||||
|
||||
+ (void)controllerViewWillAppear:(UIViewController *)controller;
|
||||
|
||||
|
||||
/**
|
||||
@deprecated Use `controllerViewDidAppear:` instead.
|
||||
*/
|
||||
|
||||
+ (void)controllerViewAppearing:(UIViewController *)controller __deprecated_msg("Use controllerViewDidAppear: instead.");
|
||||
|
||||
|
||||
/** Labelizes all `MarqueeLabel` instances that have the specified view controller in their next responder chain.
|
||||
|
||||
This method sends an `NSNotification` to all `MarqueeLabel` instances with the specified view controller in their next
|
||||
responder chain. The `labelize` property of these `MarqueeLabel` instances will be set to `YES`.
|
||||
|
||||
@param controller The view controller for which all `MarqueeLabel` instances should be labelized.
|
||||
@see labelize
|
||||
*/
|
||||
|
||||
+ (void)controllerLabelsShouldLabelize:(UIViewController *)controller;
|
||||
|
||||
|
||||
/** De-Labelizes all `MarqueeLabel` instances that have the specified view controller in their next responder chain.
|
||||
|
||||
This method sends an `NSNotification` to all `MarqueeLabel` instances with the specified view controller in their next
|
||||
responder chain. The `labelize` property of these `MarqueeLabel` instances will be set to `NO` .
|
||||
|
||||
@param controller The view controller for which all `MarqueeLabel` instances should be de-labelized.
|
||||
@see labelize
|
||||
*/
|
||||
|
||||
+ (void)controllerLabelsShouldAnimate:(UIViewController *)controller;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
1682
QXLive/Tools/MarqueeLabel.m
Executable file
1682
QXLive/Tools/MarqueeLabel.m
Executable file
File diff suppressed because it is too large
Load Diff
19
QXLive/Tools/OSS/OSSProgressModel.h
Executable file
19
QXLive/Tools/OSS/OSSProgressModel.h
Executable file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// OSSProgressModel.h
|
||||
// LNMobileProject
|
||||
//
|
||||
// Created by Reyna on 2021/1/14.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OSSProgressModel : NSObject
|
||||
|
||||
@property (nonatomic, assign) int64_t currentBytes;
|
||||
@property (nonatomic, assign) int64_t totalBytes;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
12
QXLive/Tools/OSS/OSSProgressModel.m
Executable file
12
QXLive/Tools/OSS/OSSProgressModel.m
Executable file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// OSSProgressModel.m
|
||||
// LNMobileProject
|
||||
//
|
||||
// Created by Reyna on 2021/1/14.
|
||||
//
|
||||
|
||||
#import "OSSProgressModel.h"
|
||||
|
||||
@implementation OSSProgressModel
|
||||
|
||||
@end
|
||||
16
QXLive/Tools/OSS/OSSThreadSafeMutableDictionary.h
Executable file
16
QXLive/Tools/OSS/OSSThreadSafeMutableDictionary.h
Executable file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// OSSThreadSafeMutableDictionary.h
|
||||
// LNMobileProject
|
||||
//
|
||||
// Created by Reyna on 2021/1/14.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OSSThreadSafeMutableDictionary<KeyType, ObjectType> : NSMutableDictionary
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
123
QXLive/Tools/OSS/OSSThreadSafeMutableDictionary.m
Executable file
123
QXLive/Tools/OSS/OSSThreadSafeMutableDictionary.m
Executable file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// OSSThreadSafeMutableDictionary.m
|
||||
// LNMobileProject
|
||||
//
|
||||
// Created by Reyna on 2021/1/14.
|
||||
//
|
||||
|
||||
#import "OSSThreadSafeMutableDictionary.h"
|
||||
|
||||
@interface OSSThreadSafeMutableDictionary ()
|
||||
|
||||
@property (nonatomic, strong) dispatch_queue_t queue;
|
||||
@property (nonatomic, strong) NSMutableDictionary *dict;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OSSThreadSafeMutableDictionary
|
||||
|
||||
- (instancetype)initCommon {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSString *uuid = [NSString stringWithFormat:@"com.qipao.threadSafe.dictionary_%p", self];
|
||||
_queue = dispatch_queue_create([uuid UTF8String], DISPATCH_QUEUE_CONCURRENT);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [self initCommon];
|
||||
if (self) {
|
||||
_dict = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCapacity:(NSUInteger)numItems {
|
||||
self = [self initCommon];
|
||||
if (self) {
|
||||
_dict = [NSMutableDictionary dictionaryWithCapacity:numItems];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)initWithContentsOfFile:(NSString *)path {
|
||||
self = [self initCommon];
|
||||
if (self) {
|
||||
_dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [self initCommon];
|
||||
if (self) {
|
||||
_dict = [[NSMutableDictionary alloc] initWithCoder:aDecoder];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(NSUInteger)cnt {
|
||||
self = [self initCommon];
|
||||
if (self) {
|
||||
_dict = [NSMutableDictionary dictionary];
|
||||
for (NSUInteger i = 0; i < cnt; ++i) {
|
||||
_dict[keys[i]] = objects[i];
|
||||
}
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)count {
|
||||
__block NSUInteger count;
|
||||
dispatch_sync(_queue, ^{
|
||||
count = _dict.count;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)aKey {
|
||||
__block id obj;
|
||||
dispatch_sync(_queue, ^{
|
||||
obj = _dict[aKey];
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
- (NSEnumerator *)keyEnumerator {
|
||||
__block NSEnumerator *enu;
|
||||
dispatch_sync(_queue, ^{
|
||||
enu = [_dict keyEnumerator];
|
||||
});
|
||||
return enu;
|
||||
}
|
||||
|
||||
- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey {
|
||||
aKey = [aKey copyWithZone:NULL];
|
||||
dispatch_barrier_async(_queue, ^{
|
||||
self->_dict[aKey] = anObject;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(id)aKey {
|
||||
dispatch_barrier_async(_queue, ^{
|
||||
[self->_dict removeObjectForKey:aKey];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
dispatch_barrier_async(_queue, ^{
|
||||
[self->_dict removeAllObjects];
|
||||
});
|
||||
}
|
||||
|
||||
- (id)copy {
|
||||
__block id copyInstance;
|
||||
dispatch_sync(_queue, ^{
|
||||
copyInstance = [_dict copy];
|
||||
});
|
||||
return copyInstance;
|
||||
}
|
||||
|
||||
@end
|
||||
57
QXLive/Tools/OSS/QXOSSManager.h
Executable file
57
QXLive/Tools/OSS/QXOSSManager.h
Executable file
@@ -0,0 +1,57 @@
|
||||
////
|
||||
// OSSManager.h
|
||||
// SoundRiver
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AliyunOSSiOS/AliyunOSSiOS.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, UploadImageState) {
|
||||
UploadImageFailed = 0,
|
||||
UploadImageSuccess = 1
|
||||
};
|
||||
|
||||
|
||||
@protocol QXOSSManagerDelegate;
|
||||
|
||||
@interface QXOSSManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@property (nonatomic,strong) OSSClient *client;
|
||||
|
||||
@property (nonatomic,strong) NSDateFormatter *formatter;
|
||||
|
||||
@property (nonatomic,weak) id <QXOSSManagerDelegate> delegate;
|
||||
|
||||
|
||||
- (void)uploadFile:(NSArray *_Nonnull)files withObjectKey:(NSArray *_Nullable)objectKeys isAsync:(BOOL)isAsync complete:(void(^_Nullable)(NSArray<NSString *> * _Nullable names, UploadImageState state))complete;
|
||||
|
||||
- (NSString *_Nullable)currentDate;
|
||||
|
||||
/**
|
||||
动态资源上传
|
||||
*/
|
||||
- (void)activityUploadFile:(NSArray *)files withObjectKey:(NSArray *)objectKeys isAsync:(BOOL)isAsync complete:(void(^)(NSArray<NSString *> *names, UploadImageState state))complete;
|
||||
|
||||
/**
|
||||
动态上传取消
|
||||
*/
|
||||
- (void)cancelUpload;
|
||||
|
||||
@end
|
||||
|
||||
@protocol QXOSSManagerDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
- (void)OSSManager:(QXOSSManager *)manager didUploadSuccess:(OSSTask *)task ;
|
||||
- (void)OSSManager:(QXOSSManager *)manager didUploadFailed:(OSSTask *)task;
|
||||
|
||||
- (void)OSSManagerUploadProgressWithTotalSent:(int64_t)size andTotalExpectedToSend:(int64_t)totalSize;
|
||||
|
||||
- (void)OSSManagerUploadTotalProgress:(CGFloat)totalProgress;
|
||||
@end
|
||||
|
||||
|
||||
284
QXLive/Tools/OSS/QXOSSManager.m
Executable file
284
QXLive/Tools/OSS/QXOSSManager.m
Executable file
@@ -0,0 +1,284 @@
|
||||
////
|
||||
// SocketManager.m
|
||||
// SoundRiver
|
||||
//
|
||||
//
|
||||
|
||||
#import "QXOSSManager.h"
|
||||
#import "OSSProgressModel.h"
|
||||
#import "OSSThreadSafeMutableDictionary.h"
|
||||
#import "QXApi.h"
|
||||
static QXOSSManager *sharedManager = nil;
|
||||
|
||||
@interface QXOSSManager()
|
||||
@property (nonatomic, strong) NSOperationQueue *activityUploadQueue;
|
||||
@end
|
||||
|
||||
@implementation QXOSSManager
|
||||
|
||||
+(QXOSSManager *)sharedInstance {
|
||||
@synchronized(self) {
|
||||
if(!sharedManager) {
|
||||
sharedManager = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
return sharedManager;
|
||||
}
|
||||
|
||||
+(id)allocWithZone:(NSZone *)zone{
|
||||
@synchronized(self){
|
||||
if(!sharedManager){
|
||||
//确保使用同一块内存地址
|
||||
sharedManager = [super allocWithZone:zone];
|
||||
return sharedManager;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone*)zone{
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
id<OSSCredentialProvider> credential = [[OSSPlainTextAKSKPairCredentialProvider alloc] initWithPlainTextAccessKey:OSSAccessKeyId secretKey:OSSAccessKeySecret];
|
||||
_client = [[OSSClient alloc] initWithEndpoint:[NSString stringWithFormat:@"https://%@", OSSEndPoint] credentialProvider:credential];
|
||||
_formatter = [[NSDateFormatter alloc] init];
|
||||
[_formatter setDateFormat:@"yyyy-MM-dd"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)uploadFile:(NSArray *)files withObjectKey:(NSArray *)objectKeys isAsync:(BOOL)isAsync complete:(void (^)(NSArray<NSString *> *names, UploadImageState state))complete {
|
||||
if (files.count != objectKeys.count) {
|
||||
NSLog(@"文件数量和名称数量不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
OSSThreadSafeMutableDictionary *progressModelDic = [[OSSThreadSafeMutableDictionary alloc] init];
|
||||
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
|
||||
queue.maxConcurrentOperationCount = files.count;
|
||||
|
||||
NSMutableArray *callBackNames = [NSMutableArray array];
|
||||
int i = 0;
|
||||
for (id file in files) {
|
||||
if (file) {
|
||||
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
||||
//任务执行
|
||||
NSString *keyString = objectKeys[i];
|
||||
OSSPutObjectRequest * put = [OSSPutObjectRequest new];
|
||||
put.bucketName = OSS_BUCKET_NAME;
|
||||
put.objectKey = objectKeys[i];
|
||||
[callBackNames addObject:objectKeys[i]];
|
||||
if ([file isKindOfClass:[UIImage class]]) {
|
||||
NSData *data = UIImageJPEGRepresentation(file, 0.3);
|
||||
put.uploadingData = data;
|
||||
}else if ([file isKindOfClass:[NSData class]]){
|
||||
put.uploadingData = (NSData *)file;
|
||||
}else if ([file isKindOfClass:[NSURL class]]){
|
||||
put.uploadingFileURL = (NSURL *)file;
|
||||
}
|
||||
put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
|
||||
// 当前上传段长度、当前已经上传总长度、一共需要上传的总长度
|
||||
|
||||
[self.delegate OSSManagerUploadProgressWithTotalSent:totalByteSent andTotalExpectedToSend:totalBytesExpectedToSend];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(OSSManagerUploadTotalProgress:)]) {
|
||||
OSSProgressModel *pm = [OSSProgressModel new];
|
||||
pm.currentBytes = totalByteSent;
|
||||
pm.totalBytes = totalBytesExpectedToSend;
|
||||
[progressModelDic setObject:pm forKey:keyString];
|
||||
|
||||
CGFloat totalProgress = 0;
|
||||
for (OSSProgressModel *pm in progressModelDic.allValues.reverseObjectEnumerator) {
|
||||
CGFloat singleProgress = 0;
|
||||
if (pm.currentBytes == pm.totalBytes) {
|
||||
singleProgress = 1.0 / files.count;
|
||||
}else {
|
||||
singleProgress = pm.currentBytes / (float)pm.totalBytes * (1.0 / files.count);
|
||||
}
|
||||
totalProgress = totalProgress + singleProgress;
|
||||
}
|
||||
[self.delegate OSSManagerUploadTotalProgress:totalProgress];
|
||||
}
|
||||
};
|
||||
|
||||
OSSTask * putTask = [self->_client putObject:put];
|
||||
[putTask continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
|
||||
if (!putTask.error) {
|
||||
NSLog(@"upload object success!");
|
||||
} else {
|
||||
NSLog(@"upload object failed, error: %@" , putTask.error);
|
||||
}
|
||||
if (isAsync) {
|
||||
if (file == files.lastObject) {
|
||||
NSLog(@"upload object finished!");
|
||||
if (complete) {
|
||||
complete([NSArray arrayWithArray:callBackNames] ,UploadImageSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
// [putTask waitUntilFinished]; // 阻塞直到上传完成
|
||||
|
||||
}];
|
||||
if (queue.operations.count != 0) {
|
||||
[operation addDependency:queue.operations.lastObject];
|
||||
}
|
||||
[queue addOperation:operation];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!isAsync) {
|
||||
[queue waitUntilAllOperationsAreFinished];
|
||||
NSLog(@"haha");
|
||||
if (complete) {
|
||||
if (complete) {
|
||||
complete([NSArray arrayWithArray:callBackNames], UploadImageSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)currentDate {
|
||||
return [_formatter stringFromDate:[NSDate date]];
|
||||
}
|
||||
/**
|
||||
动态资源上传
|
||||
*/
|
||||
- (void)activityUploadFile:(NSArray *)files withObjectKey:(NSArray *)objectKeys isAsync:(BOOL)isAsync complete:(void(^)(NSArray<NSString *> *names, UploadImageState state))complete
|
||||
{
|
||||
if (files.count != objectKeys.count) {
|
||||
NSLog(@"文件数量和名称数量不一致");
|
||||
return;
|
||||
}
|
||||
|
||||
OSSThreadSafeMutableDictionary *progressModelDic = [[OSSThreadSafeMutableDictionary alloc] init];
|
||||
if (self.activityUploadQueue.operations.count) {
|
||||
[self.activityUploadQueue cancelAllOperations];
|
||||
}
|
||||
self.activityUploadQueue.maxConcurrentOperationCount = files.count > 9 ? 9 : files.count;
|
||||
|
||||
NSMutableArray *callBackNames = [NSMutableArray array];
|
||||
int i = 0;
|
||||
for (id file in files) {
|
||||
if (file) {
|
||||
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
||||
//任务执行
|
||||
NSString *keyString = objectKeys[i];
|
||||
OSSPutObjectRequest * put = [OSSPutObjectRequest new];
|
||||
put.bucketName = OSS_BUCKET_NAME;
|
||||
put.objectKey = objectKeys[i];
|
||||
[callBackNames addObject:objectKeys[i]];
|
||||
if ([file isKindOfClass:[UIImage class]]) {
|
||||
NSData *data = UIImageJPEGRepresentation(file, 0.3);
|
||||
put.uploadingData = data;
|
||||
}else if ([file isKindOfClass:[NSData class]]){
|
||||
put.uploadingData = (NSData *)file;
|
||||
}else if ([file isKindOfClass:[NSURL class]]){
|
||||
put.uploadingFileURL = (NSURL *)file;
|
||||
}
|
||||
put.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
|
||||
// 当前上传段长度、当前已经上传总长度、一共需要上传的总长度
|
||||
|
||||
[self.delegate OSSManagerUploadProgressWithTotalSent:totalByteSent andTotalExpectedToSend:totalBytesExpectedToSend];
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(OSSManagerUploadTotalProgress:)]) {
|
||||
OSSProgressModel *pm = [OSSProgressModel new];
|
||||
pm.currentBytes = totalByteSent;
|
||||
pm.totalBytes = totalBytesExpectedToSend;
|
||||
[progressModelDic setObject:pm forKey:keyString];
|
||||
|
||||
CGFloat totalProgress = 0;
|
||||
for (OSSProgressModel *pm in progressModelDic.allValues.reverseObjectEnumerator) {
|
||||
CGFloat singleProgress = 0;
|
||||
if (pm.currentBytes == pm.totalBytes) {
|
||||
singleProgress = 1.0 / files.count;
|
||||
}else {
|
||||
singleProgress = pm.currentBytes / (float)pm.totalBytes * (1.0 / files.count);
|
||||
}
|
||||
totalProgress = totalProgress + singleProgress;
|
||||
}
|
||||
[self.delegate OSSManagerUploadTotalProgress:totalProgress];
|
||||
}
|
||||
};
|
||||
|
||||
OSSTask * putTask = [self->_client putObject:put];
|
||||
[putTask continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
|
||||
if (!putTask.error) {
|
||||
NSLog(@"upload object success!");
|
||||
} else {
|
||||
NSLog(@"upload object failed, error: %@" , putTask.error);
|
||||
}
|
||||
if (isAsync) {
|
||||
if (file == files.lastObject) {
|
||||
NSLog(@"upload object finished!");
|
||||
if (complete) {
|
||||
complete([NSArray arrayWithArray:callBackNames] ,UploadImageSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}];
|
||||
[putTask waitUntilFinished]; // 阻塞直到上传完成
|
||||
|
||||
}];
|
||||
if (self.activityUploadQueue.operations.count != 0) {
|
||||
[operation addDependency:self.activityUploadQueue.operations.lastObject];
|
||||
}
|
||||
[self.activityUploadQueue addOperation:operation];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!isAsync) {
|
||||
[self.activityUploadQueue waitUntilAllOperationsAreFinished];
|
||||
NSLog(@"haha");
|
||||
if (complete) {
|
||||
if (complete) {
|
||||
complete([NSArray arrayWithArray:callBackNames], UploadImageSuccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancelUpload {
|
||||
|
||||
[self.activityUploadQueue cancelAllOperations];
|
||||
}
|
||||
|
||||
//+(NSString *)getNowTimeTimestamp3{
|
||||
//
|
||||
// NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
|
||||
//
|
||||
// [formatter setDateStyle:NSDateFormatterMediumStyle];
|
||||
//
|
||||
// [formatter setTimeStyle:NSDateFormatterShortStyle];
|
||||
//
|
||||
// [formatter setDateFormat:@"YYYY-MM-dd"]; // ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
|
||||
//
|
||||
//
|
||||
// NSDate *datenow = [NSDate date];//现在时间,你可以输出来看下是什么格式
|
||||
//
|
||||
// NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];
|
||||
//
|
||||
// NSLog(@"%@",timeSp);
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// return timeSp;
|
||||
//
|
||||
//}
|
||||
|
||||
- (NSOperationQueue *)activityUploadQueue {
|
||||
if (!_activityUploadQueue) {
|
||||
_activityUploadQueue = [[NSOperationQueue alloc] init];
|
||||
}
|
||||
return _activityUploadQueue;
|
||||
}
|
||||
|
||||
@end
|
||||
114
QXLive/Tools/QXProjectTools.h
Normal file
114
QXLive/Tools/QXProjectTools.h
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// QXProjectTools.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/27.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
#import <MapKit/MKMapItem.h>//用于苹果自带地图
|
||||
|
||||
#import <MapKit/MKTypes.h>//用于苹果自带地图
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
typedef NS_ENUM(NSInteger) {
|
||||
//月日时分
|
||||
DataFormatTypeMMDDHHMM = 0,
|
||||
//月日时分 (带汉字)
|
||||
DataFormatTypeZHMMDDHHMM ,
|
||||
//时分
|
||||
DataFormatTypeHHMM,
|
||||
//年月日
|
||||
DataFormatTypeYYYYMMDD
|
||||
}DataFormatType;
|
||||
@interface QXProjectTools : NSObject
|
||||
|
||||
|
||||
/** 把字符串替换成星号*/
|
||||
+(NSString *)replaceStringWithAsterisk:(NSString *)originalStr startLocation:(NSInteger)startLocation lenght:(NSInteger)lenght;
|
||||
|
||||
/// 获取显示类型
|
||||
/// @param type 显示类型
|
||||
/// @param dateStr 原始时间字符串
|
||||
+(NSString*)getDispalyTimeWithType:(DataFormatType)type dateStr:(NSString*)dateStr;
|
||||
|
||||
|
||||
/// 获取显示日期
|
||||
/// @param dateStr 时间
|
||||
+(NSString *)getDayDisplayWithDate:(NSString *)dateStr;
|
||||
|
||||
/// 获取日期时间戳
|
||||
/// @param dateStr 日期
|
||||
+ (NSString *)getTimestampFromTime:(NSString*)dateStr;
|
||||
|
||||
|
||||
/// 打电话
|
||||
/// @param mobile 联系电话
|
||||
+(void)contactWithMobile:(NSString*)mobile;
|
||||
|
||||
/// 提示摇晃
|
||||
/// @param viewToShake view
|
||||
+ (void)shakeView:(UIView*)viewToShake;
|
||||
|
||||
|
||||
/// 跳转到已经安装的地图
|
||||
/// @param coord 目标位置
|
||||
/// @param currentCoord 当前位置
|
||||
+(UIAlertController *)getInstalledMapAppWithEndLocation:(CLLocationCoordinate2D)coord
|
||||
address:(NSString*)address
|
||||
currentLocation:(CLLocationCoordinate2D)currentCoord;
|
||||
|
||||
|
||||
+(void)contactServices;
|
||||
|
||||
|
||||
/// 振动反馈
|
||||
+(void)vibrationFeedback;
|
||||
|
||||
+(void)showMessage:(NSString*)text;
|
||||
|
||||
+(void)showMessage:(NSString *)text view:(UIView *)view;
|
||||
|
||||
+(void)showLoadingInView:(UIView*)view;
|
||||
|
||||
+(void)hideLoadingInView:(UIView*)view;
|
||||
/// 根据消息类型跳转控制器
|
||||
/// @param type 消息类型
|
||||
/// @param parameterId 参数Id
|
||||
+(void)jumpToControllerWithType:(NSInteger)type parameterId:(NSString*)parameterId;
|
||||
|
||||
|
||||
/// 展示价格
|
||||
/// @param type 0 四舍五入 1进一法 2去尾法
|
||||
/// @param number 数字
|
||||
+(NSString*)showDoubleNumberWithType:(NSInteger)type number:(double)number;
|
||||
|
||||
+(void)toSetting;
|
||||
|
||||
//阿里支付
|
||||
+(void)aliPayWithOrderNum:(NSString*)orderString
|
||||
resultBlcok:(void(^)(NSDictionary*dict))resultBlock;
|
||||
//微信支付
|
||||
+(void)wexinPayWithDict:(NSDictionary*)dict;
|
||||
|
||||
/**
|
||||
获取父控制器
|
||||
|
||||
@param childVC 子控制器
|
||||
@return 父控制器
|
||||
*/
|
||||
+(UIViewController *)getViewControllerWithChildVC:(UIViewController*)childVC;
|
||||
|
||||
/**
|
||||
获取最上层的控制器
|
||||
|
||||
@return 控制器
|
||||
*/
|
||||
+ (UIViewController*)topViewController;
|
||||
|
||||
+ (NSString *)getTimeFromTimestampWithTime:(long)time;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
476
QXLive/Tools/QXProjectTools.m
Normal file
476
QXLive/Tools/QXProjectTools.m
Normal file
@@ -0,0 +1,476 @@
|
||||
//
|
||||
// QXProjectTools.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/27.
|
||||
//
|
||||
|
||||
#import "QXProjectTools.h"
|
||||
#import <sys/utsname.h>
|
||||
#import <CoreTelephony/CTCarrier.h>
|
||||
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||
#import "NSDate+QX.h"
|
||||
//#import <WXApi.h>
|
||||
//#import <AlipaySDK/AlipaySDK.h>
|
||||
@implementation QXProjectTools
|
||||
|
||||
+(NSString *)getDispalyTimeWithType:(DataFormatType)type dateStr:(NSString *)dateStr{
|
||||
// 实例化NSDateFormatter
|
||||
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
|
||||
// 设置日期格式
|
||||
[formatter1 setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
|
||||
// NSDate形式的日期
|
||||
NSDate *date =[formatter1 dateFromString:dateStr];
|
||||
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
|
||||
|
||||
switch (type) {
|
||||
case DataFormatTypeMMDDHHMM:
|
||||
[formatter2 setDateFormat:@"MM-dd HH:mm"];
|
||||
break;
|
||||
case DataFormatTypeZHMMDDHHMM:
|
||||
[formatter2 setDateFormat:@"MM月dd日 HH:mm"];
|
||||
break;
|
||||
case DataFormatTypeHHMM:
|
||||
[formatter2 setDateFormat:@"HH:mm"];
|
||||
break;
|
||||
case DataFormatTypeYYYYMMDD:
|
||||
[formatter2 setDateFormat:@"yyyy-MM-dd"];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
NSString *dateString2 = [formatter2 stringFromDate:date];
|
||||
return dateString2;
|
||||
}
|
||||
|
||||
#pragma mark - 获取显示时间
|
||||
+(NSString *)getDayDisplayWithDate:(NSString *)dateStr{
|
||||
NSString* time = [self getTimestampFromTime:dateStr];
|
||||
NSString *str = [NSDate timeStringWithTimeInterval:time];
|
||||
return str;
|
||||
}
|
||||
//时间转时间戳
|
||||
+ (NSString *)getTimestampFromTime:(NSString*)dateStr{
|
||||
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
|
||||
[formatter setDateStyle:NSDateFormatterMediumStyle];
|
||||
|
||||
[formatter setTimeStyle:NSDateFormatterShortStyle];
|
||||
|
||||
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];// ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
|
||||
|
||||
//设置时区,这个对于时间的处理有时很重要
|
||||
|
||||
//例如你在国内发布信息,用户在国外的另一个时区,你想让用户看到正确的发布时间就得注意时区设置,时间的换算.
|
||||
|
||||
//例如你发布的时间为2010-01-26 17:40:50,那么在英国爱尔兰那边用户看到的时间应该是多少呢?
|
||||
|
||||
//他们与我们有7个小时的时差,所以他们那还没到这个时间呢...那就是把未来的事做了
|
||||
|
||||
NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
|
||||
|
||||
[formatter setTimeZone:timeZone];
|
||||
|
||||
NSDate *date =[formatter dateFromString:dateStr];
|
||||
|
||||
NSString *nowtimeStr = [formatter stringFromDate:date];//----------将nsdate按formatter格式转成nsstring
|
||||
|
||||
NSLog(@"%@", nowtimeStr);
|
||||
|
||||
// 时间转时间戳的方法:
|
||||
|
||||
NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[date timeIntervalSince1970]];
|
||||
|
||||
NSLog(@"timeSp:%@",timeSp);//时间戳的值
|
||||
|
||||
return timeSp;
|
||||
|
||||
}
|
||||
//把字符串替换成星号
|
||||
+(NSString *)replaceStringWithAsterisk:(NSString *)originalStr startLocation:(NSInteger)startLocation lenght:(NSInteger)lenght{
|
||||
NSString *newStr = originalStr;
|
||||
for (int i = 0; i < lenght; i++) {
|
||||
NSRange range = NSMakeRange(startLocation, 1);
|
||||
newStr = [newStr stringByReplacingCharactersInRange:range withString:@"*"];
|
||||
startLocation ++;
|
||||
}
|
||||
return newStr;
|
||||
}
|
||||
|
||||
#pragma mark ---- 将时间戳转换成时间
|
||||
|
||||
+ (NSString *)getTimeFromTimestampWithTime:(long)time{
|
||||
NSDate * myDate=[NSDate dateWithTimeIntervalSince1970:time];
|
||||
|
||||
//设置时间格式
|
||||
|
||||
NSDateFormatter * formatter=[[NSDateFormatter alloc]init];
|
||||
|
||||
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
|
||||
|
||||
//将时间转换为字符串
|
||||
|
||||
NSString *timeStr=[formatter stringFromDate:myDate];
|
||||
|
||||
return timeStr;
|
||||
|
||||
}
|
||||
|
||||
+(void)contactWithMobile:(NSString *)mobile{
|
||||
NSMutableString * string = [[NSMutableString alloc] initWithFormat:@"tel:%@",mobile];
|
||||
if (@available(iOS 10, *)) {
|
||||
/// 大于等于10.0系统使用此openURL方法
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string] options:@{} completionHandler:nil];
|
||||
});
|
||||
|
||||
}else{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+ (void)shakeView:(UIView*)viewToShake {
|
||||
viewToShake.backgroundColor = [UIColor redColor];
|
||||
// // 偏移值
|
||||
// CGFloat t = 5.0;
|
||||
// // 左摇
|
||||
// CGAffineTransform translateLeft =CGAffineTransformTranslate(CGAffineTransformIdentity,-t,0.0);
|
||||
// // 右晃
|
||||
// CGAffineTransform translateRight =CGAffineTransformTranslate(CGAffineTransformIdentity, t,0.0);
|
||||
// // 执行动画 重复动画且执行动画回路
|
||||
// viewToShake.transform = translateLeft;
|
||||
// [UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{
|
||||
// // 设置重复次数 repeatCount为float
|
||||
// [UIView setAnimationRepeatCount:2.0];
|
||||
// viewToShake.transform = translateRight;
|
||||
//
|
||||
// } completion:^(BOOL finished){
|
||||
// if(finished){
|
||||
// // 从视图当前状态回到初始状态
|
||||
// [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
|
||||
// viewToShake.transform =CGAffineTransformIdentity;
|
||||
// } completion:^(BOOL finished) {
|
||||
// viewToShake.backgroundColor = [UIColor whiteColor];
|
||||
// }];
|
||||
// }
|
||||
// }];
|
||||
[UIView animateWithDuration:0.1 animations:^{
|
||||
viewToShake.backgroundColor = [UIColor whiteColor];
|
||||
} completion:^(BOOL finished) {
|
||||
[UIView animateWithDuration:0.1 animations:^{
|
||||
viewToShake.backgroundColor = [UIColor redColor];
|
||||
} completion:^(BOOL finished) {
|
||||
viewToShake.backgroundColor = [UIColor whiteColor];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
+(UIAlertController *)getInstalledMapAppWithEndLocation:(CLLocationCoordinate2D)coord
|
||||
address:(NSString*)address
|
||||
currentLocation:(CLLocationCoordinate2D)currentCoord
|
||||
|
||||
{
|
||||
|
||||
// 调用地图
|
||||
|
||||
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:@"前往导航" preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
//百度地图
|
||||
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"baidumap://"]]) {
|
||||
|
||||
UIAlertAction *baiduMapAction = [UIAlertAction actionWithTitle:@"百度地图" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
NSString *baiduParameterFormat = @"baidumap://map/direction?origin=latlng:%f,%f|name:我的位置&destination=latlng:%f,%f|name:终点&mode=driving";
|
||||
|
||||
NSString *urlString = [[NSString stringWithFormat:
|
||||
|
||||
baiduParameterFormat,
|
||||
|
||||
currentCoord.latitude,//用户当前的位置
|
||||
|
||||
currentCoord.longitude,//用户当前的位置
|
||||
|
||||
coord.latitude,
|
||||
|
||||
coord.longitude]
|
||||
|
||||
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
if (@available(iOS 10, *)) {
|
||||
/// 大于等于10.0系统使用此openURL方法
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
|
||||
}else{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
}];
|
||||
|
||||
[actionSheet addAction:baiduMapAction];
|
||||
|
||||
}
|
||||
|
||||
//高德地图
|
||||
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"iosamap://"]]) {
|
||||
|
||||
UIAlertAction *gaodeMapAction = [UIAlertAction actionWithTitle:@"高德地图" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
NSString *gaodeParameterFormat = @"iosamap://path?sourceApplication=%@&sid=&slat=&slon=&sname=&did=&dlat=%f&dlon=%f&dname=&dev=0&t=0";
|
||||
|
||||
NSString *urlString = [[NSString stringWithFormat:
|
||||
|
||||
gaodeParameterFormat,
|
||||
|
||||
@"运是滴车主",
|
||||
|
||||
coord.latitude,
|
||||
|
||||
coord.longitude]
|
||||
|
||||
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
|
||||
if (@available(iOS 10, *)) {
|
||||
/// 大于等于10.0系统使用此openURL方法
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
|
||||
}else{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
}];
|
||||
|
||||
[actionSheet addAction:gaodeMapAction];
|
||||
|
||||
}
|
||||
|
||||
//苹果地图
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:@"苹果地图" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
//起点
|
||||
|
||||
MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
|
||||
|
||||
CLLocationCoordinate2D desCorrdinate = CLLocationCoordinate2DMake(coord.latitude, coord.longitude);
|
||||
|
||||
//终点
|
||||
|
||||
MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:desCorrdinate addressDictionary:nil]];
|
||||
toLocation.name = address;
|
||||
//默认驾车
|
||||
|
||||
[MKMapItem openMapsWithItems:@[currentLocation, toLocation]
|
||||
|
||||
launchOptions:@{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
|
||||
|
||||
MKLaunchOptionsMapTypeKey:[NSNumber numberWithInteger:MKMapTypeStandard],
|
||||
|
||||
MKLaunchOptionsShowsTrafficKey:[NSNumber numberWithBool:YES]}];
|
||||
|
||||
}]];
|
||||
|
||||
//腾讯地图
|
||||
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"qqmap://"]]) {
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:@"腾讯地图" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
NSString *QQParameterFormat = @"qqmap://map/routeplan?type=drive&fromcoord=%f, %f&tocoord=%f,%f&coord_type=1&policy=0&refer=%@";
|
||||
|
||||
NSString *urlString = [[NSString stringWithFormat:
|
||||
|
||||
QQParameterFormat,
|
||||
|
||||
currentCoord.latitude,//用户当前的位置
|
||||
|
||||
currentCoord.longitude,//用户当前的位置
|
||||
|
||||
coord.latitude,
|
||||
|
||||
coord.longitude,
|
||||
|
||||
@"运是滴车主"]
|
||||
|
||||
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
|
||||
if (@available(iOS 10, *)) {
|
||||
/// 大于等于10.0系统使用此openURL方法
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
|
||||
}else{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
}
|
||||
|
||||
//谷歌地图
|
||||
|
||||
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"comgooglemaps://"]]) {
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:@"谷歌地图"style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
NSString *urlString = [[NSString stringWithFormat:@"comgooglemaps://?x-source=%@&x-success=%@&saddr=&daddr=%f,%f&directionsmode=driving",
|
||||
|
||||
@"运是滴车主",
|
||||
|
||||
@"wufengTrucks",
|
||||
|
||||
coord.latitude,
|
||||
|
||||
coord.longitude]
|
||||
|
||||
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
|
||||
if (@available(iOS 10, *)) {
|
||||
/// 大于等于10.0系统使用此openURL方法
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString] options:@{} completionHandler:nil];
|
||||
}else{
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
}
|
||||
|
||||
//取消按钮
|
||||
|
||||
UIAlertAction *action3 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
|
||||
|
||||
[actionSheet dismissViewControllerAnimated:YES completion:nil];
|
||||
|
||||
}];
|
||||
|
||||
[actionSheet addAction:action3];
|
||||
|
||||
return actionSheet;
|
||||
|
||||
}
|
||||
+(void)contactServices{
|
||||
// TCallServicesView *v = [[TCallServicesView alloc] initWithFrame:CGRectMake(0, 0, 267, 255)];
|
||||
// [[TGlobal sharedInstance] handleLayoutAlertViewBlockAndAnimation:v type:(YSDPopViewTypePopFromCenter)];
|
||||
// [TGlobal sharedInstance].alertViewController.modal = YES;
|
||||
}
|
||||
|
||||
//振动反馈
|
||||
+(void)vibrationFeedback{
|
||||
UIImpactFeedbackGenerator*impactLight = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleLight];
|
||||
[impactLight impactOccurred];
|
||||
}
|
||||
|
||||
+(void)showMessage:(NSString *)text{
|
||||
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:KEYWINDOW animated:YES];
|
||||
hud.mode = MBProgressHUDModeText;
|
||||
hud.bezelView.backgroundColor = [UIColor blackColor];
|
||||
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
|
||||
hud.bezelView.color = [UIColor blackColor];
|
||||
hud.label.textColor = [UIColor whiteColor];
|
||||
hud.label.text = text;
|
||||
[hud hideAnimated:YES afterDelay:1];
|
||||
}
|
||||
+(void)showMessage:(NSString *)text view:(UIView *)view{
|
||||
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
|
||||
hud.mode = MBProgressHUDModeText;
|
||||
hud.bezelView.backgroundColor = [UIColor blackColor];
|
||||
hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
|
||||
hud.bezelView.color = [UIColor blackColor];
|
||||
hud.label.textColor = [UIColor whiteColor];
|
||||
hud.label.text = text;
|
||||
[hud hideAnimated:YES afterDelay:1];
|
||||
}
|
||||
+(void)showLoadingInView:(UIView*)view{
|
||||
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
|
||||
hud.mode = MBProgressHUDModeIndeterminate;
|
||||
}
|
||||
+(void)hideLoadingInView:(UIView*)view{
|
||||
[MBProgressHUD hideHUDForView:view animated:YES];
|
||||
}
|
||||
+(void)jumpToControllerWithType:(NSInteger)type parameterId:(NSString *)parameterId{
|
||||
|
||||
}
|
||||
+(NSString *)showDoubleNumberWithType:(NSInteger)type number:(double)number{
|
||||
NSString *str = @"0.00";
|
||||
if (type == 0) {
|
||||
long result = (long)roundf(number*100);
|
||||
str = [NSString stringWithFormat:@"%.2f",result/100.0];
|
||||
}else if (type == 1){
|
||||
long result = (long)ceilf(number*100);
|
||||
str = [NSString stringWithFormat:@"%.2f",result/100.0];
|
||||
}else if (type == 2){
|
||||
long price = number*100;
|
||||
long result = (long)floor(price);
|
||||
str = [NSString stringWithFormat:@"%.2f",result/100.0];
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
+(void)toSetting{
|
||||
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
|
||||
if([[UIApplication sharedApplication] canOpenURL:url]) {
|
||||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+ (UIViewController*)topViewController{
|
||||
return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
|
||||
}
|
||||
|
||||
|
||||
+(UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController{
|
||||
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
|
||||
UITabBarController *tabBarController = (UITabBarController *)rootViewController;
|
||||
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
|
||||
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
|
||||
UINavigationController* navigationController = (UINavigationController*)rootViewController;
|
||||
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
|
||||
} else if (rootViewController.presentedViewController) {
|
||||
UIViewController* presentedViewController = rootViewController.presentedViewController;
|
||||
return [self topViewControllerWithRootViewController:presentedViewController];
|
||||
} else {
|
||||
return rootViewController;
|
||||
}
|
||||
}
|
||||
|
||||
//获取父控制器
|
||||
+(UIViewController *)getViewControllerWithChildVC:(UIViewController*)childVC{
|
||||
for (UIView* next = [childVC.view superview]; next; next = next.superview) {
|
||||
UIResponder *nextResponder = [next nextResponder];
|
||||
if ([nextResponder isKindOfClass:[UIViewController class]]) {
|
||||
return (UIViewController *)nextResponder;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
//+(void)aliPayWithOrderNum:(NSString*)orderString
|
||||
// resultBlcok:(void(^)(NSDictionary*dict))resultBlock{
|
||||
// [[AlipaySDK defaultService] payOrder:orderString fromScheme:@"QXLive" callback:^(NSDictionary *resultDic) {
|
||||
// NSLog(@"reslut = %@",resultDic);
|
||||
// resultBlock(resultDic);
|
||||
// }];
|
||||
//}
|
||||
//+(void)wexinPayWithDict:(NSDictionary*)dict{
|
||||
//// [WXApi registerApp:dict[@"appid"] enableMTA:YES];
|
||||
//// [WXApi registerApp:dict[@"appid"] universalLink:@"https://"]
|
||||
// //需要创建这个支付对象
|
||||
// PayReq *req = [[PayReq alloc] init];
|
||||
// //由用户微信号和AppID组成的唯一标识,用于校验微信用户
|
||||
// req.openID = dict[@"appid"];
|
||||
// // 商家id,在注册的时候给的
|
||||
// req.partnerId = dict[@"partnerid"];
|
||||
// // 预支付订单这个是后台跟微信服务器交互后,微信服务器传给你们服务器的,你们服务器再传给你
|
||||
// req.prepayId = dict[@"prepayid"];
|
||||
// req.package = dict[@"package"];
|
||||
// // 随机编码,为了防止重复的,在后台生成
|
||||
// req.nonceStr = dict[@"noncestr"];
|
||||
// // 这个是时间戳,也是在后台生成的,为了验证支付的
|
||||
// req.timeStamp = [dict[@"timestamp"] intValue];
|
||||
// // 这个签名也是后台做的
|
||||
// req.sign = dict[@"sign"];
|
||||
// [WXApi sendReq:req completion:^(BOOL success) {
|
||||
//
|
||||
// }];
|
||||
//}
|
||||
|
||||
@end
|
||||
28
QXLive/Tools/QXTextView.h
Normal file
28
QXLive/Tools/QXTextView.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// QXTextView.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/15.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QXTextView : UITextView
|
||||
@property (nonatomic,strong)UILabel *placehoulderLabel;
|
||||
/// 颜色 默认为 #9B9B9B
|
||||
@property (nonatomic,strong)UIColor* placehoulderColor;
|
||||
///
|
||||
@property (nonatomic,strong)NSString* placehoulder;
|
||||
/// 默认为系统字体 12号字
|
||||
@property (nonatomic,strong)UIFont* placehoulderFont;
|
||||
/// 最大字数限制 默认 120
|
||||
@property (nonatomic,assign)NSInteger maxLength;
|
||||
/// 默认 NO
|
||||
@property (nonatomic,assign)BOOL maxLengthHidden;
|
||||
/// 剩余可输入文字颜色
|
||||
@property (nonatomic,strong)UIColor *lengthColor;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
149
QXLive/Tools/QXTextView.m
Normal file
149
QXLive/Tools/QXTextView.m
Normal file
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// QXTextView.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/5/15.
|
||||
//
|
||||
|
||||
#import "QXTextView.h"
|
||||
@interface QXTextView()<UITextViewDelegate>
|
||||
|
||||
@property (nonatomic,strong)UILabel *maxCountLabel;
|
||||
@end
|
||||
@implementation QXTextView
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self initSubview];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self initSubview];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)initSubview{
|
||||
self.lengthColor = QXConfig.themeColor;
|
||||
self.maxLength = 120;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidChange:) name:UITextViewTextDidChangeNotification object:nil];
|
||||
self.delegate = self;
|
||||
[self addSubview:self.placehoulderLabel];
|
||||
|
||||
self.returnKeyType = UIReturnKeyDone;
|
||||
[self addSubview:self.maxCountLabel];
|
||||
|
||||
}
|
||||
-(void)layoutSubviews{
|
||||
[super layoutSubviews];
|
||||
self.placehoulderLabel.frame = CGRectMake(10, 10, self.width-20, 50);
|
||||
[self.placehoulderLabel sizeToFit];
|
||||
self.maxCountLabel.frame = CGRectMake(10, self.height-20, self.width-20, 15);
|
||||
}
|
||||
-(void)textViewDidBeginEditing:(UITextView *)textView{
|
||||
if (textView.text.length == 0) {
|
||||
self.placehoulderLabel.hidden = NO;
|
||||
}else{
|
||||
self.placehoulderLabel.hidden = YES;
|
||||
}
|
||||
}
|
||||
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
|
||||
|
||||
if([text isEqualToString:@"\n"]){
|
||||
|
||||
[textView resignFirstResponder];
|
||||
|
||||
return NO;
|
||||
|
||||
}
|
||||
|
||||
return YES;
|
||||
|
||||
}
|
||||
|
||||
-(void)textViewDidChange:(UITextView*)textView{
|
||||
|
||||
if (self.text.length == 0) {
|
||||
self.placehoulderLabel.hidden = NO;
|
||||
}else{
|
||||
self.placehoulderLabel.hidden = YES;
|
||||
}
|
||||
|
||||
if (self.text.length > self.maxLength) {
|
||||
self.text = [self.text substringToIndex:self.maxLength];
|
||||
return;
|
||||
}
|
||||
if (self.maxLengthHidden) {
|
||||
return;
|
||||
}
|
||||
NSString *length = [NSString stringWithFormat:@"%ld",self.maxLength - self.text.length];
|
||||
NSString *str = [NSString stringWithFormat:@"%@%@%@",QXText(@"还可输入"),length,QXText(@"字")];
|
||||
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str];
|
||||
[attr yy_setColor:self.lengthColor range:[str rangeOfString:length]];
|
||||
self.maxCountLabel.attributedText = attr;
|
||||
}
|
||||
|
||||
-(void)setMaxLength:(NSInteger)maxLength{
|
||||
_maxLength = maxLength;
|
||||
NSString *length = [NSString stringWithFormat:@"%ld",self.maxLength - self.text.length];
|
||||
NSString *str = [NSString stringWithFormat:@"%@%@%@",QXText(@"还可输入"),length,QXText(@"字")];
|
||||
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str];
|
||||
[attr yy_setColor:self.lengthColor range:[str rangeOfString:length]];
|
||||
self.maxCountLabel.attributedText = attr;
|
||||
}
|
||||
|
||||
-(void)setMaxLengthHidden:(BOOL)maxLengthHidden{
|
||||
_maxLengthHidden = maxLengthHidden;
|
||||
self.maxCountLabel.hidden = YES;
|
||||
}
|
||||
-(void)setPlacehoulderColor:(UIColor *)placehoulderColor{
|
||||
_placehoulderColor = placehoulderColor;
|
||||
self.placehoulderLabel.textColor = placehoulderColor;
|
||||
}
|
||||
-(void)setPlacehoulder:(NSString *)placehoulder{
|
||||
_placehoulder = placehoulder;
|
||||
self.placehoulderLabel.text = placehoulder;
|
||||
}
|
||||
-(void)setPlacehoulderFont:(UIFont *)placehoulderFont{
|
||||
_placehoulderFont = placehoulderFont;
|
||||
self.placehoulderLabel.font = placehoulderFont;
|
||||
}
|
||||
-(void)setLengthColor:(UIColor *)lengthColor{
|
||||
_lengthColor = lengthColor;
|
||||
}
|
||||
|
||||
-(UILabel *)placehoulderLabel{
|
||||
if (!_placehoulderLabel) {
|
||||
_placehoulderLabel = [[UILabel alloc] init];
|
||||
_placehoulderLabel.numberOfLines = 0;
|
||||
_placehoulderLabel.font = [UIFont systemFontOfSize:12.f];
|
||||
_placehoulderLabel.textColor = RGB16(0x9B9B9B);
|
||||
MJWeakSelf
|
||||
[_placehoulderLabel addTapBlock:^(id _Nonnull obj) {
|
||||
[weakSelf becomeFirstResponder];
|
||||
}];
|
||||
}
|
||||
return _placehoulderLabel;
|
||||
}
|
||||
|
||||
-(UILabel *)maxCountLabel{
|
||||
if (!_maxCountLabel) {
|
||||
_maxCountLabel = [[UILabel alloc] init];
|
||||
_maxCountLabel.font = [UIFont systemFontOfSize:9];
|
||||
_maxCountLabel.textAlignment = NSTextAlignmentRight;
|
||||
_maxCountLabel.textColor = RGB16(0x666666);
|
||||
NSString *length = [NSString stringWithFormat:@"%ld",self.maxLength - self.text.length];
|
||||
NSString *str = [NSString stringWithFormat:@"%@%@%@",QXText(@"还可输入"),length,QXText(@"字")];
|
||||
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str];
|
||||
[attr yy_setColor:self.lengthColor range:[str rangeOfString:length]];
|
||||
_maxCountLabel.attributedText = attr;
|
||||
}
|
||||
return _maxCountLabel;
|
||||
}
|
||||
@end
|
||||
26
QXLive/Tools/QXTimer.h
Normal file
26
QXLive/Tools/QXTimer.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// QXTimer.h
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface QXTimer : NSObject
|
||||
+ (QXTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block;
|
||||
|
||||
|
||||
- (instancetype)initWithInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block;
|
||||
- (void)invalidate;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
58
QXLive/Tools/QXTimer.m
Normal file
58
QXLive/Tools/QXTimer.m
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// QXTimer.m
|
||||
// QXLive
|
||||
//
|
||||
// Created by 启星 on 2025/4/28.
|
||||
//
|
||||
|
||||
#import "QXTimer.h"
|
||||
@interface QXTimer ()
|
||||
|
||||
@property (strong, nonatomic) dispatch_source_t timer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QXTimer
|
||||
|
||||
+ (QXTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block {
|
||||
QXTimer *timer = [[QXTimer alloc] initWithInterval:interval
|
||||
repeats:repeats
|
||||
queue:queue
|
||||
block:block];
|
||||
return timer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithInterval:(NSTimeInterval)interval
|
||||
repeats:(BOOL)repeats
|
||||
queue:(dispatch_queue_t)queue
|
||||
block:(void (^)(void))block {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
||||
dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
|
||||
dispatch_source_set_event_handler(self.timer, ^{
|
||||
if (!repeats) {
|
||||
dispatch_source_cancel(self.timer);
|
||||
}
|
||||
block();
|
||||
});
|
||||
dispatch_resume(self.timer);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)invalidate {
|
||||
if (self.timer) {
|
||||
dispatch_source_cancel(self.timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user