首次提交

This commit is contained in:
启星
2025-09-22 18:48:29 +08:00
parent 28ae935e93
commit ae9be0b58e
8941 changed files with 999209 additions and 2 deletions

40
QXLive/Tools/CKShimmerLabel.h Executable file
View 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
View 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

View 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

View 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];
//nowDateselfDate
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];
//nowDateselfDate
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

View 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 (显示的是一周的第几天weekday1为周日。)
[@"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

View 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

View 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

View 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 {
/**
* titleEdgeInsetstitleinsettableViewcontentInset
* titlebuttonimage
* imagelabelimagebuttonlabeltitlebuttonimage
*/
CGFloat spacef = ceilf(space);
// 1.imageViewtitleLabel
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, *)) {
// iOS8titleLabelsize0
labelWidth = self.titleLabel.intrinsicContentSize.width;
labelHeight = self.titleLabel.intrinsicContentSize.height;
}else {
labelWidth = self.titleLabel.frame.size.width;
labelHeight = self.titleLabel.frame.size.height;
}
// 2.imageEdgeInsetslabelEdgeInsets
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.stylespacefimageEdgeInsetslabelEdgeInsets
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

View 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

View 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
//0x2
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];
}
//alpha1
+ (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

View 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

View 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; /*ignoreEventno, */
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

View 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

View 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

View 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

View 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,x2y6(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];
//layoutCAGradientLayer
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;
//layoutCAGradientLayer
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];
//layoutCAGradientLayer
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;
//layoutCAGradientLayer
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 {
/*
layoutIfNeededlayoutSubviews
setNeedsLayoutlayoutSubviewsrunloop
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
View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -0,0 +1,12 @@
//
// OSSProgressModel.m
// LNMobileProject
//
// Created by Reyna on 2021/1/14.
//
#import "OSSProgressModel.h"
@implementation OSSProgressModel
@end

View 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

View 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
View 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
View 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"]; // ----------,hhHH: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

View 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

View 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"];// ----------,hhHH: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];//----------nsdateformatternsstring
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:^{
// // repeatCountfloat
// [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
View 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
View 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
View 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
View 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