持续更新...
1.单击手势和双击手势冲突的解决
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
//example usage:
[singleTap requireGestureRecognizerToFail:doubleTap];
2.AutoLayout中的baseline对齐
通常是对有文本显示功能的两个或者多个视图进行约束,NSLayoutAttributeLastBaseline
文本的最后一行、NSLayoutAttributeFirstBaseline
文本的第一行,在UIView
中有viewForFirstBaselineLayout
、viewForLastBaselineLayout
两个只读属性,在自定义的视图中,重写它们的getter
方法返回某一子视图,然后就可以对这个自定义的视图使用baseline
约束了。
3.OC中的范型
为了书写方便,我们在定义数组、字典等容器类型的变量时,常这样写NSArray
在类型后加尖括号,括号内表示元素类型,在使用到某一个元素时,可以直接调用方法,像这样:_categorys.lastObject.length
。在赋初值时,如果元素类型不匹配,Xcode会有警告。其中的关键是ObjetctType
,在NSArray
的头文件中@interface NSArray<__covariant ObjectType>
是这样写的,自定义一个类,如下:
@interface Test : NSObject
- (void)addObj:(ObjetctType)obj;
@end
注意,我在尖括号中只加入了
ObjetctType
,而没有添加__covariant
关键字,__covariant
关键字表示协变(让一个带有协变参数的泛型接口(或委托)可以接收类型更加精细化,具体化的泛型接口(或委托)作为参数,可以看成OO中多态的一个延伸。),因为有这样一个关键字,NSArray
可以做以下操作:
而
Test
不可以:
另外与__covariant
相对的是__contravariant
逆变(让一个带有协变参数的泛型接口(或委托)可以接收粒度更粗的泛型接口或委托作为参数,这个过程实际上是参数类型更加精细化的过程。)
协变让一个粗粒度接口(或委托)可以接收一个更加具体的接口(或委托)作为参数(或返回值);逆变让一个接口(或委托)的参数类型(或返回值)类型更加具体化,也就是参数类型更强,更明确。
通常,协变类型参数可用作委托的返回类型,而逆变类型参数可用作参数类型。对于接口,协变类型参数可用作接口的方法的返回类型,而逆变类型参数可用作接口的方法的参数类型。
ps.此处黑体字部分参考博客:一句话清晰总结协变(covariant)和逆变 (contravariant)
4.iOS11中搜索框的新特性
UINavigationItem
@available(iOS 11.0, *)
open var searchController: UISearchController?
5.swift中的值、变量和常量
值 (value)是不变的,永久的,它从不会改变。比如,1, true 和 [1,2,3] 都是值。这些是字面量 (literal)的例子,值也可以是运行时生成的。当你计算 5 的平方时,你得到的数字也是一个值。
当我们使用 var x = [1,2] 来将一个值进行命名的时候,我们实际上创建了一个名为 x 的变量 (variable)来持有 [1,2] 这个值。通过像是执行 x.append(3) 这样的操作来改变 x 时,我们并没有改变原来的值。相反,我们所做的是使用 [1,2,3] 这个新的值来替代原来 x 中的内容。可能实际上它的内部实现真的只是在某段内存的后面添加上一个条目,并不是全体的替换,但是至少从逻辑上来说值是全新的。我们将这个过程称为变量的改变 (mutating)。
我们还可以使用 let 而不是 var 来声明一个常量变量 (constant variables),或者简称为常量。一旦常量被赋予一个值,它就不能再次被赋一个新的值了。
摘录来自: Chris Eidhof. “Swift 进阶”。
6.在使用git clone某一个库的时候如果出现如下错误:
error: RPC failed; curl 18 transfer closed with outstanding read data remaining
....
错误的原因是git库的缓存空间不足,无法clone过大的库
可在当前目录下执行如下命令:
git config http.postBuffer 524288000
524288000表示524288000B,即500MB,具体的数值可根据不同状况来进行调整。
7.字符串含有中文转URL
//iOS9之前
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
//iOS9之后
NSCharacterSet *set = [[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "] invertedSet];
urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:set];
8.富文本高度计算不准确
NSDictionary *dic = @{NSFontAttributeName:detail.font, NSParagraphStyleAttributeName:paragraphStyle, NSKernAttributeName:@0
};
CGFloat h = [str boundingRectWithSize:CGSizeMake(kScreenWidth-48.0, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:NULL].size.height;
在属性字典中添加NSKernAttributeName:@0
键值对
9.使用正则表达式截取html、xml标签内容
应用场景:当从后台获取文字并展示超链接富文本时,需要获取到url
和文本内容
以yue博客
为例
NSString *replaceKey = @"yue博客";
if ([content containsString:replaceKey]) {
NSRegularExpression *hrefRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=href=\").+(?=\")" options:kNilOptions error:NULL];
NSRegularExpression *textRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=>).+(?=<)" options:kNilOptions error:NULL];
NSTextCheckingResult *hrefResult = [hrefRegex firstMatchInString:content options:kNilOptions range:NSMakeRange(0, content.length)];
NSTextCheckingResult *textResult = [textRegex firstMatchInString:content options:kNilOptions range:NSMakeRange(0, content.length)];
if (hrefResult && textResult && hrefResult.range.location != NSNotFound && textResult.range.location != NSNotFound) {
href = [content substringWithRange:hrefResult.range];
text = [content substringWithRange:textResult.range];
}
//去掉标签内容
content = [content stringByReplacingOccurrencesOfString:@"" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange (0, content.length)];
content = [content stringByReplacingOccurrencesOfString:@"<\\/a>" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange (0, content.length)];
keyRange = [content rangeOfString:text];
}
此处代码取自YYKitDemo,我根据自身情况去掉了(?<=href=\").+(?=\" )
末尾的空格。
此时已知富文本内容、超链接、range,创建NSMutableAttributedString
添加属性即可,点击事件可以使用UITextView
或其他第三方控件来做。
(?<=href=\").+(?=\")
和firstMatchInString
方法适用于只有一个标签的场景,当有多个标签时需要在(?=\")
前加一个?
表示非贪婪匹配,并将firstMatchInString
方法换成matchesInString
方法,得出的是一个元素为NSTextCheckingResult
类型的数组
NSString *content = @"yue博客\
yue博客\
yue博客\
yue博客";
NSRegularExpression *hrefRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=href=\").+?(?=\")" options:kNilOptions error:NULL];
NSRegularExpression *textRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=>).+?(?=<)" options:kNilOptions error:NULL];
NSArray *hrefResults = [hrefRegex matchesInString:content options:kNilOptions range:NSMakeRange(0, content.length)];
NSArray *textResults = [textRegex matchesInString:content options:kNilOptions range:NSMakeRange(0, content.length)];
for (NSTextCheckingResult *href in hrefResults) {
if (href.range.location != NSNotFound) {
NSLog(@"%@",[content substringWithRange:href.range]);
}
}
for (NSTextCheckingResult *text in textResults) {
if (text.range.location != NSNotFound) {
NSLog(@"%@",[content substringWithRange:text.range]);
}
}
10.禁止UITableView
的reload动画,解决闪动问题
动画选项设置为UITableViewRowAnimationNone
时,会有闪动现象
[UIView performWithoutAnimation:^{
[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:3 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}];
11.APP防止接口被抓包
向后台或者运维要一下网络证书,用钥匙串打开,导出为cer文件,拖至项目内,在创建AFHTTPSessionManager
或者AFURLSessionManager
时做如下处理
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey];
sessionManager.securityPolicy = policy;
证书验证有如下两种方式,我使用的是AFSSLPinningModePublicKey
,因为这样可以避免证书到期需要APP重新发版的问题。(2018-12-18证书到期替换新证书后,公钥也会变,在此不建议使用此方法做APP防止接口被抓包处理)
AFSSLPinningModePublicKey,//公钥验证,不做有效期验证
AFSSLPinningModeCertificate,//做有效期验证
如果项目中没有使用AFN
的话,可在NSURLSessionDelegate
和NSURLSessionTaskDelegate
方法
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
和- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
中进行设置,具体的验证步骤,请参考AFNetWorking
内的做法
AFURLSessionManager.m
NSURLSessionDelegate ->- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
NSURLSessionTaskDelegate ->- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
AFSecurityPolicy.m
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust);
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust);
11.模仿iPhone自带地图的列表滑动效果
demo地址
12.判断5G网络的一个坑:iOS14.0CTRadioAccessTechnologyNRNSA 、CTRadioAccessTechnologyNR
闪退
通常的判断方法是,在官方API中有两个字符串表示5G,并标注了API_AVAILABLE(ios(14.0))
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyNRNSA API_AVAILABLE(ios(14.0)) API_UNAVAILABLE(macos);
CORETELEPHONY_EXTERN NSString * const CTRadioAccessTechnologyNR API_AVAILABLE(ios(14.0)) API_UNAVAILABLE(macos);
但是如果使用如下方法进行判断,那么在iOS14.0、14.0.1版本上则会收获一个crash
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
if (@available(iOS 14.0, *)) {
if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyNRNSA] || [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyNR]) {
// 5G
}
}
将if (@available(iOS 14.0, *))
改为if (@available(iOS 14.1, *))
即可
13.Use of unimplemented initializer 'init(nibName:bundle:)
混编项目在iOS12中,swift的navigationController、viewController会报Use of unimplemented initializer 'init(nibName:bundle:)'错误,需要在重写init(rootViewController:)、init()
方法的同时,重写init(nibName:bundle:)
方法
** “Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.”
Automatic Initializer Inheritance
Rule 1: If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2: If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.
参考