iOS一些小技巧 | 四

一,上传gif动图的时候,需要不解码的Data数据,需要通过路径获取转换成Data:

//
//上传gif图
//bundle中
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"motion" ofType:@"GIF"] ];
//手机资源中
NSString *url = [info objectForKey:UIImagePickerControllerImageURL];
NSData *imageData = [NSData dataWithContentsOfFile:url];

//以下方式转换上传的gif图没有动态效果
UIImagePNGRepresentation(image);
UIImageJPEGRepresentation(image, 1.0);
//

二,collectionview滑动固定其滑动距离,分页滑动:

//
//
@interface SkyVideoCoueseLayout : UICollectionViewFlowLayout
@property (nonatomic,assign)CGFloat dragShort;
@end
//
//

#import "SkyVideoCourseLayout.h"

@implementation SkyVideoCourseLayout

- (void)prepareLayout
{
    [super prepareLayout];
    if(self.dragShort <= 0){
        return;
    }
    if(self.collectionView != nil){
        self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast;
        //CGSize size = [[self.collectionView valueForKey:@"interpageSpacing"] CGSizeValue];
        //if(!CGSizeEqualToSize(CGSizeZero, size)){
            if(self.scrollDirection == UICollectionViewScrollDirectionHorizontal){
                NSValue *sizeValue = [NSValue valueWithCGSize:CGSizeMake(-self.dragShort, 0.0)];
                [self.collectionView setValue:sizeValue forKey:@"interpageSpacing"];
            }else{
                NSValue *sizeValue = [NSValue valueWithCGSize:CGSizeMake(0.0, -self.dragShort)];
                [self.collectionView setValue:sizeValue forKey:@"interpageSpacing"];
            }
        //}
        //CGPoint origin = [[self.collectionView valueForKey:@"pagingOrigin"] CGPointValue];
        //if(!CGPointEqualToPoint(CGPointZero, origin)){
            NSValue *originValue = [NSValue valueWithCGPoint:CGPointZero];
            [self.collectionView setValue:originValue forKey:@"pagingOrigin"];
        //}
    }
}

@end

三,collectionview cell出现的时候产生动画:

//
//代理
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
   // 这个方法会在 cell 即将显示在屏幕上时调用
   // 你可以在这里执行缩小动画

   // 假设你要为 cell 添加缩小动画
   if ([cell isKindOfClass:[UICollectionViewCell class]]) {
       UICollectionViewCell *cellToAnimate = (UICollectionViewCell *)cell;
       
       // 缩小为原始尺寸的 80%
       cellToAnimate.transform = CGAffineTransformMakeScale(0.8, 0.8);
       
       [UIView animateWithDuration:0.3 animations:^{
           // 恢复原始尺寸
           cellToAnimate.transform = CGAffineTransformIdentity;
       }];
   }
}
//
//

四,网络监测工具,实时监测网络环境,网络强弱,主要思路是每几秒钟请求一次网络,如果请求成功,则网络连接成功,如果请求失败,则标识网络连接失败。
具体内部的重点是:
1,请求阿里的DNS公网地址:
NSString *const SkyAlibabaPublicDNS = @”https://223.5.5.5/”;
2,timeoutIntervalForResource和timeoutIntervalForRequest属性是设置当前请求的超时时间;
3,_urlSession = nil;重置session,主要是为了重置超时时间,否则超时时间不准确;
4,SkyPublicTimerTool为计时器封装工具;

具体代码如下:

//
//
//.h文件
//
//  SkyNetworkDetectionTool.h
//  Course
//
//  Created by skyzizhu on 2023/12/5.
//  Copyright © 2023 Ashes. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger,SkyNetworkDetectionState)
{
    SkyNetworkDetectionUnknown = 0,//未知
    SkyNetworkDetectionWifi = 2,//wifi
    SkyNetworkDetection2G,//2G
    SkyNetworkDetection3G,//3G
    SkyNetworkDetection4G,//4G
    SkyNetworkDetection5G,//5G
};

@interface SkyNetworkDetectionTool : NSObject
+ (instancetype)detection;
@property (nonatomic,assign)NSTimeInterval timeoutInterval;
///开始检测,reachable:是否有网,state:哪种网络类型
- (void)startDetectionResult:(void(^_Nullable)(SkyNetworkDetectionState state,BOOL reachable))result;
- (void)stop;
@end

NS_ASSUME_NONNULL_END

//
//.m文件
//
//  SkyNetworkDetectionTool.m
//  Course
//
//  Created by skyzizhu on 2023/12/5.
//  Copyright © 2023 Ashes. All rights reserved.
//

#import "SkyNetworkDetectionTool.h"
#import "Reachability.h"
#import "SkyPublicTimerTool.h"

NSString *const SkyAlibabaPublicDNS = @"https://223.5.5.5/";

@interface SkyNetworkDetectionTool()<NSURLSessionDelegate>
@property (nonatomic,strong,nullable)SkyPublicTimerTool *timerTool;
@property (nonatomic,strong,nullable)Reachability *reachability;
@property (nonatomic,strong,nullable)NSURLSession *urlSession;
@property (nonatomic,copy,nullable)void(^networkStateDetectionResultHandle)(SkyNetworkDetectionState state,BOOL reachable);
//是否有网络
@property (nonatomic,assign)BOOL isReachable;
@end

@implementation SkyNetworkDetectionTool


+ (instancetype)detection
{
    static SkyNetworkDetectionTool *detection = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        detection = [[SkyNetworkDetectionTool alloc] init];
    });
    return detection;
}
- (instancetype)init
{
    self = [super init];
    if(self){
        self.timerTool = [[SkyPublicTimerTool alloc]init];
        self.timerTool.timeInterval = self.timeoutInterval;
        self.reachability = [Reachability reachabilityForInternetConnection];
        //默认有网
        self.isReachable = YES;
    }
    return self;
}
- (NSTimeInterval)timeoutInterval
{
    if(0.0 == _timeoutInterval){
        _timeoutInterval = 2.0;
    }
    return _timeoutInterval;
}
///开始检测
- (void)startDetectionResult:(void(^_Nullable)(SkyNetworkDetectionState state,BOOL reachable))result
{
    
    self.networkStateDetectionResultHandle = result;
    
    //
    __weak typeof(self) weakSelf = self;
    [self.timerTool startTheTimer];
    [self.timerTool setDoingTimerHandle:^(NSInteger duration) {
        
        // 获取当前网络状态
        NetworkStatus networkStatus = [weakSelf.reachability currentReachabilityStatus];
        // 处理当前网络状态
        [weakSelf handleNetworkStatus:networkStatus];
    }];
}

- (void)handleNetworkStatus:(NetworkStatus)networkStatus {
    SkyNetworkDetectionState netState = SkyNetworkDetectionUnknown;
    switch (networkStatus) {
        case NotReachable:
            netState = SkyNetworkDetectionUnknown;
            break;
            
        case ReachableViaWiFi:
            netState = SkyNetworkDetectionWifi;
            break;
            
        case ReachableViaWWAN:
        {
            // 根据网络类型判断是2G还是3G
            CTTelephonyNetworkInfo *telephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init];
             NSString *currentRadioAccessTechnology = telephonyNetworkInfo.currentRadioAccessTechnology;
             if (currentRadioAccessTechnology) {
                 if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
                     [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge]) {
                     netState = SkyNetworkDetection2G;
                 } else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
                            [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
                     netState = SkyNetworkDetection3G;
                 } else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
                     netState = SkyNetworkDetection4G;
                 } else{
                     if (@available(iOS 14.1, *)) {
                         if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyNR]) {
                             netState = SkyNetworkDetection5G;
                         }
                     }
                 }
             }
        }
            break;
            
        default:
            break;
    }
    if(networkStatus == NotReachable){
        self.isReachable = NO;
    }else{
        __weak typeof(self) weakSelf = self;
        [self hostReachable:SkyAlibabaPublicDNS complate:^(NSError * _Nullable error) {
            weakSelf.isReachable = (error == nil);
        }];;
    }
    
    if(self.networkStateDetectionResultHandle){
        self.networkStateDetectionResultHandle(netState, self.isReachable);
    }
}

- (void)hostReachable:(NSString *)host complate:(void(^_Nullable)(NSError  * _Nullable error))complate
{
    NSURL *url = [NSURL URLWithString:host];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDataTask *dataTask = [self.urlSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            if(complate){
                complate(error);
            }
        });
    }];
    [dataTask resume];
}

- (void)stop
{
    [self.timerTool invalidate];
    self.networkStateDetectionResultHandle = nil;
    self.isReachable = YES;
}

- (NSURLSession *)urlSession
{
    _urlSession = nil;
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
    configuration.timeoutIntervalForResource = self.timeoutInterval;
    configuration.timeoutIntervalForRequest = self.timeoutInterval;
    _urlSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    return _urlSession;
}

@end

//
//

五,如何获取collectionview的头部视图(section):

//
//
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
SkyShowHomeHeaderView *reusableView =  [subController.collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"SkyShowHomeHeaderViewID" forIndexPath:indexPath];
//
//

六,如何获取tableview的头部视图(section):

//
//
SkyFilterListSectionView *sectionView = (SkyFilterListSectionView *)[self.tableView headerViewForSection:0];
return sectionView;
//通过headerViewForSection方法获取
//

七,navigationController销毁当前页面,并push到下一个页面:

//
//
// 销毁当前页面,并跳转到预约成功页面
NSMutableArray *viewControllers = self.navigationController.viewControllers.mutableCopy;
//销毁当前页面
[viewControllers removeLastObject];
//跳转的新页面
SkyConsultAppointmentSucceedController *appoSucVc = [[SkyConsultAppointmentSucceedController alloc]init];
//
[viewControllers addObject:appoSucVc];
[self.navigationController setViewControllers:viewControllers.copy animated:YES];
//
//

八,Git中查看某一个文件具体修改了哪些内容:

//git diff filepath
//查看当前已修改且未提交的文件跟最后一次提交时修改的区别
git diff Course/Course.xcodeproj/project.pbxproj
//
//查看两次提交前后的区别,3a0e0e075为提交1,9d49b66c7为提交2
git diff 3a0e0e075 9d49b66c7 Course/Course.xcodeproj/project.pbxproj

九,OC中的异常捕获,try-catch:

//try块中的代码如果抛出异常,这个异常会被@catch块捕获,然后异常的reason属性会被打印出来。无论是否发生异常,finally块中的代码都会被执行。
//
    @try {
        NSMutableArray *array = [NSMutableArray array];
        NSString *nilElem = nil;
        [array addObject:nilElem];
        NSLog(@"%@",array);
    } @catch (NSException *exception) {
        NSLog(@"@catch - - %@",exception.reason);
    } @finally {
        NSLog(@"@finally");
    }
//
//

十,OC中一些异常机制处理,NSString:

//
//.h
@interface NSString (SkyException)
/// substringWithRange
- (NSString *_Nullable)substringWithRange:(NSRange)range error:(NSError *_Nonnull*_Nullable)error;
/// substringToIndex
- (NSString *_Nullable)substringToIndex:(NSInteger)index error:(NSError *_Nonnull*_Nullable)error;
/// substringFromIndex
- (NSString *_Nullable)substringFromIndex:(NSInteger)index error:(NSError *_Nonnull*_Nullable)error;
@end
//
//.m
#import "NString+SkyException.h"

typedef NS_ENUM(NSInteger,SkyNSStringExceptionErrorCode)
{
    SkyStringSubstringWithRangeErrorCode = 100001,
    SkyStringSubstringToIndexErrorCode = 100002,
    SkyStringSubstringFromIndexErrorCode = 100003,
};

@implementation NSString (SkyException)

- (NSString *_Nullable)substringWithRange:(NSRange)range error:(NSError *_Nonnull*_Nullable)error
{
    if (NSMaxRange(range) <= [self length]) {
        NSString *substring = [self substringWithRange:range];
        return substring;
    }else{
        NSError *curError = [NSError errorWithDomain:NSCocoaErrorDomain code:SkyStringSubstringWithRangeErrorCode userInfo:@{NSLocalizedDescriptionKey: @"Index out of bounds from substringWithRange"}];
        if (error != NULL) {
            *error = curError;
        }
        return nil;
    }
}

- (NSString *_Nullable)substringToIndex:(NSInteger)index error:(NSError *_Nonnull*_Nullable)error
{
    if (index <= [self length]) {
        NSString *substring = [self substringToIndex:index];
        return substring;
    }else{
        NSError *curError = [NSError errorWithDomain:NSCocoaErrorDomain code:SkyStringSubstringToIndexErrorCode userInfo:@{NSLocalizedDescriptionKey: @"Index out of bounds from substringToIndex"}];
        if (error != NULL) {
            *error = curError;
        }
        return nil;
    }
}

- (NSString *_Nullable)substringFromIndex:(NSInteger)index error:(NSError *_Nonnull*_Nullable)error
{
    if (index <= [self length]) {
        NSString *substring = [self substringFromIndex:index];
        return substring;
    }else{
        NSError *curError = [NSError errorWithDomain:NSCocoaErrorDomain code:SkyStringSubstringFromIndexErrorCode userInfo:@{NSLocalizedDescriptionKey: @"Index out of bounds from substringFromIndex"}];
        if (error != NULL) {
            *error = curError;
        }
        return nil;
    }
}

@end
//



//使用:
//    // 原始字符串
    NSString *str = @"Hello, World!";
    // 尝试获取范围为 (50, 1) 的子字符串
    NSRange range = NSMakeRange(1, 24);
    NSError *error = nil;
    NSString *substring = [str substringToIndex:100 error:&error];
    if (error) {
        // 处理错误
        NSLog(@"Error: %@ - %ld", error.localizedDescription,error.code);
    } else {
        // 成功获取子字符串
        NSLog(@"Substring: %@", substring);
    }

十二,根据图片宽度,设置图片比例不变,宽高比根据屏幕宽度计算:

//
//
CGFloat width = self.bounds.size.width;
CGFloat scale = width / self.detailImageView.image.size.width;
CGFloat height = scale * self.detailImageView.image.size.height;
self.detailImageView.frame = CGRectMake(0.0, 0.0, width, height);
//
//

十三,属性、返回值、参数等修饰的一些关键字作用:

1,Nullable,可以用于属性、方法返回值、方法参数中;
表示可以为空;
2,Nonnull,可以用于属性、方法返回值、方法参数中;
表示不为空;
3,null_resettable,可以用于属性、方法返回值、方法参数中;
set可以传入为空,get方法不能返回nil,必须要处理为空情况,重写get方法;
4,null_unspecified,可以用于属性、方法返回值、方法参数中;
不确定是否为空;

十四,代码内部判断swift的版本:

//#if swift(>=4.2)  #else  #endif
//
  private var data: Data? {
    #if swift(>=4.2)
      return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)
    #else
      return self.pngData() ?? self.jpegData(compressionQuality: Constant.jpegCompressionQuality)
    #endif  // swift(>=4.2)
  }
//

十五,判断当前是否获取摄像头库权限:

//
//
UIImagePickerController.isSourceTypeAvailable(.camera)
//
//

Leave a Reply

Required fields are marked *