iOS一些小功能记录
从网上看到和平时用到一些iOS代码小功能记录下,方便后续查阅
----------------------------持续更新------------------------
1.UIView截图
- (UIImage *)getImageWithView:(UIView *)view {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
//获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//渲染
[view.layer renderInContext:ctx];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
//生成UIView形式的View
- (nullable UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
2.UIView设置阴影
- 设置为 masksToBounds=YES,阴影不显示;
- 设置阴影时 view 的 frame 是否为 CGRectZero,如果是,设置阴影后修改 frame,也不会显示阴影;自动布局时 frame 为 CGRectZero 时,可以使用 layoutIfNeeded
- 高效的阴影实现是为阴影指定 shadowPath
//设置阴影颜色
self.testView.layer.shadowColor = [UIColor blueColor].CGColor;
//设置阴影透明度
self.testView.layer.shadowOpacity = 0.8;
//阴影圆角
self.testView.layer.shadowRadius = 1;
//设置阴影偏离位置
self.testView.layer.shadowOffset = CGSizeMake(10, 2);
UIEdgeInsets edges = UIEdgeInsetsMake(8, 10, 0, 0);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(edges.left, edges.top, CGRectGetWidth(self.testView2.frame), CGRectGetHeight(self.testView2.frame))];
//通过 shadowPath 设置阴影,可以自定义阴影的形状,这个方式可以避免离屏渲染
self.testView2.layer.shadowPath = path.CGPath;
self.testView2.layer.shadowColor = [UIColor blueColor].CGColor;
//设置阴影透明度
self.testView2.layer.shadowOpacity = 0.8;
//阴影圆角
self.testView2.layer.shadowRadius = 1;
3.设置圆角
- 原生圆角效果带来的离屏渲染开销。直接使用圆角的素材,或者提前在子线程将图片进行圆角裁剪,
- 还有一种思路是在需要圆角的视图最上层添加一个中空的圆角遮罩层,以此来做出圆角效果。这个遮罩层和被盖在下面的视图在显示时会由 GPU 进行图层混合,而图层混合的开销远小于离屏渲染
//给某个角度设置圆角 同样会触发离屏幕渲染
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.testView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.testView.bounds;
maskLayer.path = maskPath.CGPath;
self.testView.layer.mask = maskLayer;
//四个角设置圆角
CAShapeLayer *mask = [CAShapeLayer new];
mask.path = [UIBezierPath bezierPathWithRoundedRect:self.testView2.bounds cornerRadius:10].CGPath;
self.testView2.layer.mask = mask;
详细参考iOS设置圆角的四种方法
4.查看打印类的私有方法
-
_shortMethodDescription 可以显示所有接收者的实例和类方法
-
_methodDescription 和_shortMethodDescription一样, 包括父类的方法
-
_ivarDescription 可以显示 接收者的成员变量,包括类型和值
-
recursiveDescription 打印某个视图的层级关系
UIView *view = [[UIView alloc] init];
NSLog(@"_ivarDescription%@", [view performSelector:@selector(_ivarDescription)]);
NSLog(@"_methodDescription =%@", [view performSelector:@selector(_methodDescription)]);
NSLog(@"_shortMethodDescription =%@", [view performSelector:@selector(_shortMethodDescription)]);
结果如图:
_ivarDescription
_shortMethodDescription
参考:
Extended Type Info in Objective-C
三个打印类信息的私有方法
Powerful private methods for debugging in Cycript & LLDB
5.iOS 9 以后通知不再需要手动移除
603C343F-CA1C-4C1C-93C7-65A4EDE43EFF.png6.判断iPhone异形屏几种方式
- 通过获取设备的device model来判断
- 通过获取屏幕宽高来判断。(注意横竖屏)
- 通过底部安全区域来判断
if (@available(iOS 11, *)) {
UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
if (keyWindow == nil) {
keyWindow = [[UIApplication sharedApplication] delegate].window;
}
CGFloat bottomSafeInset = keyWindow.safeAreaInsets.bottom;
//竖屏34 横屏21.f
if (bottomSafeInset == 34.f || bottomSafeInset == 21.f) {
NSLog(@"我是异形屏");
}
}
//宏定义 判断是否是iPhone异形屏
#define isIPhoneXSeries()\
^(){\
BOOL iPhoneXSerie = NO;\
if (UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPhone) {\
return iPhoneXSerie;\
}\
if (@available(iOS 11.0, *)) {\
UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window];\
if (mainWindow.safeAreaInsets.bottom > 0.0) {\
iPhoneXSerie = YES;\
}\
}\
return iPhoneXSerie;\
}()
- 在设备处于竖屏且没有隐藏导航栏的时候,可以通过获取导航栏高度判断
CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
if (statusBarHeight == 44.f) {
NSLog(@"我是异形屏");
}
- 最后看到别人说的还可以通过判断是否支持FaceID判断,如果用户没有禁止使用的话。需要导入头文件
<LocalAuthentication/LocalAuthentication.h>
UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom];
if (idiom == UIUserInterfaceIdiomPhone) {
if (@available(iOS 11, *)) {
LAContext *laContext = [[LAContext alloc] init];
if ([laContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
if (laContext.biometryType == LABiometryTypeFaceID) {
NSLog(@"我是异形屏");
}
}
}
}
7.iOS9 以后字符串转换API
- (nullable NSString *)stringByApplyingTransform:(NSStringTransform)transform reverse:(BOOL)reverse API_AVAILABLE(macos(10.11), ios(9.0), watchos(2.0), tvos(9.0));
支持拼音繁体简体等转换。
NSString *stringTest = @"我是一个测试";
NSString * changeIntoPinYin = [stringTest stringByApplyingTransform:NSStringTransformToLatin reverse:NO];
NSLog(@"汉字转拼音= %@",changeIntoPinYin);
//去掉结合符号
NSLog(@"去掉结合符号 = %@",[changeIntoPinYin stringByApplyingTransform:NSStringTransformStripCombiningMarks reverse:NO]);
NSLog(@"简体转繁体= %@",[stringTest stringByApplyingTransform:@"Hans-Hant" reverse:NO]);
打印结果
可参考
ICU Text Transforms in Foundation
Cocoa中的ICU文本转换
ICU - International Components for Unicode
8.为了防止遍历等其他的操作的时候内存压力过大,导致崩溃异常,可手动控制这些对象的生命周期,在适当的位置加上@autoreleasepool{}
9.使用runtime
动态创建类
举个例子我这里创建一个继承FitfunBaseService
的类。
#import <objc/runtime.h>
NSString * offlineApiBaseUrl(id self,SEL _cmd1){
return objc_getAssociatedObject([self class], "apiServiceBaseUrl");
}
void createServiceClass(NSString *className,NSString *baseUrl) {
const char * myClassName;
myClassName = [className UTF8String];
Class pClass = NSClassFromString(className);
if (NSClassFromString(className) || NSClassFromString(@"FitfunBaseService") == nil) {
return;
}
//创建类
pClass = objc_allocateClassPair(NSClassFromString(@"FitfunBaseService"), myClassName, 0);
objc_setAssociatedObject(pClass, "apiServiceBaseUrl", baseUrl, OBJC_ASSOCIATION_COPY);
//为类创建方法
class_addMethod(pClass, @selector(offlineApiBaseUrl), (IMP)offlineApiBaseUrl, "@@:");
//注册类,使其能被引用。
objc_registerClassPair(pClass);
}
10.runtime交换方法
void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果原有方法存在,添加就会失败
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// 如果原有方法不存在添加一个方法
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
void swizzleMethodDifferentClasses(Class originalClass,Class swizzledClass, SEL originalSelector, SEL swizzledSelector) {
//类方法交换
Method originalMethod = class_getClassMethod(originalClass, originalSelector);
Method swizzledMethod = class_getClassMethod(swizzledClass, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
11.除去数组重复元素
NSArray *oldArray = @[@"1",@"3",@"1",@{@"1":@"2"},@{@"1":@"2"},@{@"1":@"3"}];
NSArray *newArray = [oldArray valueForKeyPath:@"@distinctUnionOfObjects.self"];
NSLog(@"oldArray =%@,newArray =%@",oldArray, newArray);
//输出结果
oldArray =(
1,
3,
1,
{
1 = 2;
},
{
1 = 2;
},
{
1 = 3;
}
),newArray =(
1,
3
)
关于数组更多的kvc用法可参考高效开发iOS -- 那些不为人知的KVC
12.修改textField的placeholder的字体颜色、大小
[textField setValue:[UIColor greenColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont boldSystemFontOfSize:15] forKeyPath:@"_placeholderLabel.font"];
<==>等价
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:
placeholder attributes:
@{NSFontAttributeName : [UIFont systemFontOfSize:15],
NSForegroundColorAttributeName : [UIColor greenColor]}];
13.环境变量 DYLD_PRINT_STATISTICS
打印出程序启动过程中各个阶段所消耗的时间,DYLD_PRINT_LIBRARIES
(启动前和启动后的)和 DYLD_PRINT_LIBRARIES_POST_LAUNCH
(启动后的),打印 dyld 加载的库
强调:DYLD_PRINT_STATISTICS 变量打印时间是iOS10以后才支持的功能,所以需要用iOS10系统及以上的机器来做测试。
控制台打印结果
强烈推荐看iOS启动时间优化 ,讲的非常不错
14.更容易看懂宏
对于宏定义,可以使用xcode自带对单个文件预处理功能
Produce -> Perform Action -> Preprocess 'xxxx.m'
预处理后 预处理后
15.获取UIImage 图片的一部分
UIImage *image = [UIImage imageNamed:@"test.png"];
CGImageRef imageRef = image.CGImage;
//这里xy和宽高相对于图片原始坐标来说来说的,比如我的图片现在宽高941x719,他的原始坐标就是(0,0,941,719)
//取从图片从坐标(16,110)开始宽高都是290的局部
CGRect imageRect = CGRectMake(160, 110, 290, 290);
CGImageRef imageRectRef = CGImageCreateWithImageInRect(imageRef, imageRect);
UIImage *newImage = [[UIImage alloc] initWithCGImage:imageRectRef];
self.testImageView.image = newImage;
16.给UIView设置image
UIImage *image = [UIImage imageNamed:@"test.png"];
CGImageRef imageRef = image.CGImage;
self.testView.layer.contents = (__bridge id)imageRef;
//设置拉伸范围
self.testView.layer.contentsCenter = CGRectMake(0.8, 0.8, 1, 1);