一、结构体的使用struct
结构体能更加方便的定义好不同的属性,我们使用起来更方便,减少了我们使用中进一步去手动获取数据。
1、定义结构体:
struct TYTCommonResponse {
NSUInteger code;
NSString *_Nullable msg;
id _Nullable data; //上面三个手动拆分的数据
id _Nullable rowData; // 接口返回原数据
};
声明结构体:
typedef struct TYTCommonResponse TYTCommonResponse;
12、使用结构体回传数据
比如我们封装好网络请求,response的数据需要进一步解析。
- (void)responseStructWithURLString:(NSString *)path onSuccess:(nullable void (^)(struct TYTCommonResponse))success responseObject:(id _Nullable)responseObject
{
TYTCommonResponse resultObj;
resultObj.code = [responseObject[@"code"] integerValue];
resultObj.msg = responseObject[@"msg"];
resultObj.data = responseObject[@"data"];
resultObj.rowData = responseObject;
TYTLog(@"返回数据(%@):%@", path, [NSString stringWithFormat:@"\ncode: %zd\nmsg: %@\nrowData: %@", resultObj.code, resultObj.msg, resultObj.rowData]);
[TYTResultCodeHandler handleMessageWithCode:resultObj.code data:responseObject];
if (success) {
success(resultObj);
}
}
二、WKWebView中的Cookie
Cookie是我们日常浏览的网页页面为了识别具体的用户身份,而在request Header加带的一种数据。有了这个数据,你要浏览的的网页就知道你是哪个用户。
举个例子:如果浏览网站的时候没有Cookie或者Cookie失效了,那么就会出现这种情况。你隔了好多天访问微博网页,点击的标签明明之前保存的是你登录之后的微博首页,可以直接看到你的好友动态。但是现在它却给你跳转了登录页面先要你登录。就是因为你Cookie失效导致的。微博没办法判断你是否是有效用户了。
另外,我们需要特别注意,在页面跳转的时候,网站有时候会自己在request Header中加入一些Cookie,也作为身份校验的一部分。
Cookie可以由native端设置也可以由服务端来设置。
客户端想要打印出webView中的Cookie,就有四个地方可选了:
1、native端设置Cookie的时候
2、获取request Header
3、获取document.cookie的内容
4、获取response header中的set-cookie
WKWebView中设置Cookie的方案:
Cookie版本适配方案
适配iOS11.0~iOS11.2
苹果在iOS11.0推出了WKHTTPCookieStore,这个类对WKWebView的效果和NSHttpCookieStorage对UIWebView的效果一样,并且也是零延迟。在请求发起的时候,我们只需要将Cookie设置到WKHTTPCookieStore就可以了。
// 1. 设置Cookie
WKWebsiteDataStore *store = [WKWebsiteDataStore defaultDataStore];
[store.httpCookieStore setCookie:cookie completionHandler:^{
NSLog(@“cookie添加成功”);
}];
// 2. 加载请求
[self.wkwebView loadRequest: request];
适配iOS11.3
WKHTTPCookieStore第一次设置Cookie之后,发起请求,Cookie不能同步到request Header中。此时,webView重新请求页面,WKHTTPCookieStore的作用恢复正常,之后请求其他的webView也不会出现Cookie丢失的问题。 总结就是在iOS11.3中,WKHTTPCookieStore设置Cookie之后,第一次发起请求Cookie会丢失,第二次及以后的请求的Cookie没有问题。
解决方式是,我们在执行loadRequest之前主动设置一次Cookie。我这里是在didFinishLaunchOption:中设置的。设置完之后,就没有问题了,注意,他是单利奥。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
WKWebsiteDataStore *store = [WKWebsiteDataStore defaultDataStore];
[store.httpCookieStore setCookie:cookie completionHandler:^{
NSLog(@“cookie添加成功”);
}];
}
三、Bundle 和 xcassets 的主要区别
简述:
1、工程中所有使用 Asset Catalog 管理的图片(在 .xcassets 文件夹下),最终输出的时候,都会被压缩到 Assets.car 内。
2、反之,不在 Assets.car 内的,我们将它统一归类为 Bundle 管理的。
Bundle 和 xcassets 的主要区别有:
1、xcassets 里面的图片,只能通过 imageNamed 加载。Bundle 还可以通过 imageWithContentsOfFile 等方式。
2、xcassets 里的 2x 和 3x,会根据具体设备分发,不会同时包含。而 Bundle 会都包含。(App Slicing)
3、xcassets 内,可以对图片进行 Slicing,即裁剪和拉伸。Bundle 不支持。所以,Bundle会使包的体积变大。
4、Bundle 内支持多语言,xcassets 不支持。
xcassets使用 imageNamed 创建的 UIImage,会立即被加入到 NSCache 中(解码后的 Image Buffer),直到收到内存警告的时候,才会释放不在使用的 UIImage。
而 Bundle的imageWithContentsOfFile方式,它每次都会重新申请内存,相同图片不会缓存。
所以,xcassets 内的图片,加载后会产生缓存,Bundle则不会,所以在开发中,常用的,较小的图,应该放在 xcassets 内管理。而大图应该放在 Bundle 内管理。
三、宏定义调用方法
以block的方式调用,每次换行都要添加\
,否则报错。
#pragma mark - 适配ios11 和iPhone X
#define isiPhoneX \
({BOOL isPhoneX = NO;\
if (@available(iOS 11.0, *)) {\
isPhoneX = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\
}\
(isPhoneX);})
// 判断是否是iPhone 5
#define isiPhone5 Screen_Width == 320
// iPhoneX的状态栏的高度
//#define TYT_StatusBarHeight (isiPhoneX ? 44.f : 20.f)
#define TYT_StatusBarHeight \
^(){\
if (@available(iOS 13.0, *)) {\
UIStatusBarManager *statusBarManager = [[[UIApplication sharedApplication] windows] objectAtIndex:0].windowScene.statusBarManager;\
return statusBarManager.statusBarFrame.size.height;\
}else{\
return [[UIApplication sharedApplication] statusBarFrame].size.height;\
}\
}()
// iPhoneX的导航栏的高度
#define TYT_NavBarHeight \
^(){\
if (@available(iOS 13.0, *)) {\
UIStatusBarManager *statusBarManager = [[[UIApplication sharedApplication] windows] objectAtIndex:0].windowScene.statusBarManager;\
return statusBarManager.statusBarFrame.size.height+44;\
}else{\
return [[UIApplication sharedApplication] statusBarFrame].size.height+44;\
}\
}()
四、Block作为返回值使用
__nullable
:可以为空
这里遇到一个崩溃,设置为nil,打断点发现completion是null,就是但是completion没有判空,导致崩溃,加了判空就可以了。
[self startAreaSelViewAnimation:nil];
#pragma mark -- 开始动画
- (void)startAreaSelViewAnimation:(void (^ __nullable)(BOOL finished))completion{
[UIView animateWithDuration:0.25 animations:^{
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7];
_backGroundView.frame = CGRectMake(0, _backGroundView.frame.origin.y, Screen_Width, _newBgHeight);//locationView.y+locationView.height
} completion:^(BOOL finished) {
if (completion != nil) {
completion(finished);
}
}];
}
五、Xcode 14.3升级后运行报错
模拟器:
ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a
clang: error: linker command failed with exit code 1 (use -v to see invocation)
真机:
ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a
clang: error: linker command failed with exit code 1 (use -v to see invocation)
由于Xcode 14.3版本移除了ARC相关的库,从而导致一些默认部署目标是iOS 8版本的第三方库出现报错。只要最低部署目标不低于iOS 9版本,运行项目时就不会去链接ARC相关的库,也就不会出现找不到库的报错。
解决方法
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
end
end
end
11.0是你支持的最低版本,并不是固定的。
解决方案链接
六、自定义AlertView的文字颜色
Title使用富文本。
alterView使用KVC设置颜色。
- (void)createWaitActivateAlertView{
NSString *message = @"您的企业账户未完成激活,不能发”专票“货源,请尽快激活。激活之前,您只能发布不开票或普票货源。";
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:message];
[attrStr addAttribute:NSForegroundColorAttributeName
value:kUIColorFromRGB(0x666666)
range:NSMakeRange(0, 28)];
[attrStr addAttribute:NSForegroundColorAttributeName
value:kUIColorFromRGB(0xFE9D09)
range:NSMakeRange(attrStr.length-19, 19)];
[attrStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12] range:NSMakeRange(0, attrStr.length)];
UIAlertController *alterView = [UIAlertController alertControllerWithTitle:@"您的企业账户待激活" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancle = [UIAlertAction actionWithTitle:@"暂不激活" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}];
[alterView setValue:attrStr forKey:@"attributedMessage"];
[cancle setValue:kUIColorFromRGB(0x999999) forKey:@"titleTextColor"];
UIAlertAction *sure = [UIAlertAction actionWithTitle:@"去激活" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
}];
[sure setValue:kUIColorFromRGB(0x3194EF) forKey:@"titleTextColor"];
[alterView addAction:cancle];
[alterView addAction:sure];
[self presentViewController:alterView animated:YES completion:nil];
}
七、限定键盘输入的内容
TextField只能输入字母和数字+字数限制。
传送门
1、设置键盘样式
self.textField.keyboardType = UIKeyboardTypeASCIICapable;
2、设置宏条件
#define NUM @"0123456789"
#define ALPHA @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
#define ALPHANUM @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
3、textFieldDelegate代理方法
// 只能输入字母和数字
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ALPHANUM] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
return [string isEqualToString:filtered];
}
// 小写字母转大写字母
- (void)textFieldDidEndEditing:(UITextField *)textField {
textField.text = [textField.text uppercaseString];
}
八、组件化block传参
需求一个组件调用另一个组件,需要获取返回的参数,有两种方式,一个是delegate,另一个就是block。
1、delegate
通过设置代理,使用responseSelect的方式直接调用方法,方法返回数据之后,使用代理回传参数。调用中CTM的时候不需要在调用VC中声明代理。
1.1 中间件方法
- (UIViewController *)Main_Car_complaintShowBigImagesViewControllerWithDelegate:(id)delegate dataArray:(NSArray *)dataArray index:(NSInteger)index isHidden:(BOOL)isHidden {
NSMutableDictionary *params = @{}.mutableCopy;
if (delegate) {
params[@"delegate"] = delegate;
}
params[@"index"] = @(index);
return [self performTarget:MainApp_Car_TargetName action:@"complaintShowBigImagesViewController" params:params shouldCacheTarget:NO];
}
1.2 组件中被调用的事件
- (UIViewController *)Action_complaintShowBigImagesViewController:(NSDictionary *)params {
id data = params[@"data_array"];
if (![data isKindOfClass:[NSArray class]]) {
return nil;
}
NSArray *images = (NSArray *)data;
TYTComplaintShowBigImagesViewController *viewImage = [[TYTComplaintShowBigImagesViewController alloc] init];
viewImage.delegate = params[@"delegate"];
viewImage.dataArray = [NSMutableArray arrayWithArray:images];
viewImage.isHidden = [params[@"is_hidden"] boolValue];
viewImage.currentIndex = [params[@"index"] intValue];
return viewImage;
}
2、block
2.1 中间件方法:
- (void)Shipment_shipViewControllerExcellentCar:(void(^)(NSDictionary *response,BOOL isExcellentCar))callBack{
NSMutableDictionary *params = [NSMutableDictionary new];
if(callBack){
params = @{
@"block": callBack
}.mutableCopy;
}
[CT() performTarget:Shipment_TargetName action:@"shipmmentManagerExcellentCar" params:params shouldCacheTarget:NO];
}
2.2 组件内被CTM调用的方法:
- (void)Action_shipmmentManagerExcellentCar:(NSDictionary *)params
{
TYTShipmmentManager *manager = [[TYTShipmmentManager alloc] init];
void (^callBack)(NSDictionary *response, BOOL isExcellentCar) = params[@"block"];
if (callBack) {
manager.callBack = callBack;
}
[manager queryExcellentCarResult:^(NSDictionary *response, BOOL isExcellentCar) {}];
}
2.3 TYTShipmmentManager网络请求赋值
注意,这里的self.callBack(GlobalBlock)一定要有值,否则会崩溃,所以如果CTM没有调用传参复制的话,我们就需要手动复制。
- (void)queryExcellentCarResult:(TYTGShipmmentManagerBlock)callBack{
if (self.callBack == nil) {
self.callBack = callBack;
}
callBack = self.callBack;
[[TYTCommonRequestManager sharedManager] fetchResponseMethod:GET forPlatURLString:@"deposit/queryEquity.action" parameters:@{} success:^(struct TYTCommonResponse response) {
if(response.code == 200){
NSDictionary *dataDic = (NSDictionary *)response.data;
if( [dataDic[@"isJoin"] intValue] == 1 && [dataDic[@"blackStatus"] intValue] == 0 && [dataDic[@"authStatus"] intValue] == 1){
self.callBack(dataDic, YES);
}else{
self.callBack(dataDic, NO);
}
}else{
self.callBack(@{},NO);
}
} failure:^(NSError * _Nonnull error) {
self.callBack(@{},NO);
}];
}
网友评论