一些iOS工作中的技巧

1,248 阅读4分钟

算来自己工作4年有余,一直在iOS开发中重复造车轮。最近总结下自己在工作中的新知识,以后不定期更新。

一、按钮防重复点击

这个范畴不光是按钮了,有很多中情况,比如按钮重复点击,网络重复请求,跳转页面重复等很多情况,大概说以下之前我的解决思路。

1.按钮重复点击常见的思路是控制按钮的交互状态,也就是按钮交互功能的开和关(enable属性),但是前提就是你需要获得开启和状态关闭的状态,手动处理。第二种思路是利用可控延迟,让之前重复点击的功能无效化。在搜索中经常见到,就是输入文字的时候不访问网络,输入完成停止下来以后在访问,调用方法为:

//取消调用的方法

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(click) object:nil];

//半秒后调用方法

[self performSelector:@selector(click) withObject:nil afterDelay:0.5];

2.网络重复请求我们常见的思路就是在访问网络开始和结束增加hud,或者专门建立一个网络请求的页面(view)盖在屏幕上,遮挡其他控件。

3.今天着重说下iOS页面push跳转这种情况,这种情况一般不做处理,但是狂点的话,经常造成重复进入页面的问题,很多大的App也存在这个情况,我用微信狂点跳转页面,出现几率还是挺高的。自己总结了一个思路,思路如下:

如果你的Navigation是自定义的,可以重写-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated方法,在此方法中做处理

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {

if (![[super topViewController] isKindOfClass:[viewController class]]) {

[super pushViewController:viewController animated:animated]; 

}

} 这样可以有效的防止重复进入一个一样的controller,但是也有弊端,如果你的需求就是要重复进入一个类,就是被这块的逻辑拦截。

二、iOS中App内部的数据在外部获取

这个需求不太常见,但是很有用,常见的情况,项目A中的字符串@"123"如何能被项目B中获取,我整理几个我的思路:

1.利用系统自身的复制粘贴版,项目A中复制一段可见于UI上的数据,在另一个App中粘贴,需求也很明确,就是需要这个元素存在在UI上。

2.写入沙盒的数据提取,直接放代码吧

//存入一段字符串

_path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"file.txt"];

[@"哈哈哈" writeToFile:_path atomically:YES encoding:NSUTF8StringEncoding error:nil] ? NSLog(@"成功") : NSLog(@"失败");

//打开这个类

UIDocumentInteractionController *documentVC = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:_path]];

documentVC.UTI = @"com.microsoft.word.doc";

documentVC.delegate = self;

[documentVC presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];

这是模拟器状态,真机中会多大量的选项,备忘录、QQ、微信等等很多,思路是把沙盒中的数据借用其他工具执行,我们有个需求就是下载word、excel文档,然后再通过微信打开,就是这个思路

3.keychain,这个思路我自己思考了以下感觉是可以的,但是没有实际用过。

三、利用runtime获取网络状态

我们一般获取网络状态都是Reachability,AFNetworking等工具,然后看到一种思路利用runtime便利属性,可以拿到状态栏中的一个属性:

Ivar *ivars = class_copyIvarList([UIApplication sharedApplication].class, &count);

在for循环打印会拿到一个statusBar属性

id statusBar = [[UIApplication sharedApplication] valueForKeyPath:@"statusBar"];

再次遍历属性,拿到_foregroundView这个属性

Ivar *ivars = class_copyIvarList([statusBar class], &count);

再次遍历这个属性

NSArray *childrenArray = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];

可以得到网络状态的视图UIStatusBarDataNetworkItemView,信号的视图(几格信号)UIStatusBarServiceItemView,电池状态的视图UIStatusBarBatteryItemView,显示时间的视图UIStatusBarTimeItemView

我们需要的就是UIStatusBarDataNetworkItemView,还需要遍历,最终可以拿到dataNetworkType这个属性

Ivar *ivars = class_copyIvarList([NSClassFromString(@"UIStatusBarDataNetworkItemView") class], &count)

id type = [obj valueForKeyPath:@"dataNetworkType"];

这个就是我们需要的最终属性了,其实了解的话也就可以不用rumtime来遍历了,可以直接写,写一个精简版本:

NSArray *children;

if (self.view.bounds.size.height >= 812) {

children = [[[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];

}else {

children = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];

}

[children enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

if ([obj isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {

NSLog(@"%d", [[obj valueForKeyPath:@"dataNetworkType"] intValue]);

}

}];

注: 打印出的type数字对应的网络状态依次是:0 - 无网络; 1 - 2G; 2 - 3G; 3 - 4G; 5 - WIFI

建议: 将获取的UIStatusBarDataNetworkItemView保存起来,定时去取它的dataNetworkType,这样就可以实时监控网络状态啦(KVO在这里是行不通的哟)

当然,此方法存在一定的局限性,比如当状态栏被隐藏的时候,无法使用此方法。

目前在现在最新的xr,xs,xsMax机型上可能属性有变化获取不到,这种写法可以为大家提供一些思路