iOS 的基本控件
作为iOS的基本控件,我们首要能想到的就是UIButton,UILabel,UITextField等等。这些控件基本都属于UIKit库里面的。官方对于UIKit是这么解释的
UIKit框架为您的iOS或tvOS应用程序提供所需的基础结构。 它提供了用于实现界面的窗口和视图架构,用于向应用程序提供Multitouch和其他类型输入的事件处理基础架构,以及管理用户,系统和应用程序之间交互所需的主要运行循环。 该框架提供的其他功能包括动画支持,文档支持,绘图和打印支持,有关当前设备的信息,文本管理和显示,搜索支持,可访问性支持,应用程序扩展支持和资源管理。除非另有说明,否则仅从应用程序的主线程或主调度队列中使用UIKit类。 此限制特别适用于从UIResponder派生的类或涉及以任何方式操纵应用程序的用户界面的类。
那么UIButton作为其中的一个类,我们当时做的最基础的初始化就是用
buttonWithType: 这个方法
为什么不用init呢,是因为我们创建按钮的时候往往需要用到按钮不同的类型,但是UIButton的规则就是创建后不能改变其类型,只能创建时更改。所以我们就常用上面的方法了。按钮创建后,我们知道他有几种状态,默认,选择,高亮什么的,这里就不一一叙述,有一些小点很有意思,但平时很少用到
@property(nonatomic) BOOL adjustsImageWhenHighlighted;
这个方法就是控制高亮状态下,按钮上面的图片是否变亮的,但是我们由于经常自定义控件,所以就不会经常出现按钮上面的图片需要高亮的情况,同时我们用titleEdgeInset和ImageEdgeInset来调整文字和图片距离边界的距离.
那么工作中有两种需求比较常见,一种按钮不能重复点击,一种是获取触摸事件,还有一种是放大按钮的点击作用域。第一种是因为很多点击按钮发动网络请求,如果重复点击可能导致多次发送请求,同时如果有蒙版的话也会弹多次。第二种是我们有时候这个按钮有点击事件,但是我们不希望获取到这个按钮的点击事件,可能希望获取到按钮的父View的点击事件,那么这种就要用hitTest,还有放大按钮的点击作用域,这种确切的说其实是iOS的响应链问题,我们下面分别讨论。
第二种响应链问题,单单从UI的角度来说,响应链就是告诉你你触摸的屏幕上面的UI是如何响应你的。比方说A上面有个B·,你摸了B,那么系统就会先问A有人摸你吗,A说摸了,系统再问,你上面还有人吗,A说有B。于是系统再问B,有人摸你吗,直到问到最上层为止,下面是我从网上找的一张图和代码,我们可以看清楚原理

//in every view .m overide those methods
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"进入A_View---hitTest withEvent ---");
UIView * view = [super hitTest:point withEvent:event];
NSLog(@"离开A_View--- hitTest withEvent ---hitTestView:%@",view);
return view;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {
NSLog(@"A_view--- pointInside withEvent ---");
BOOL isInside = [super pointInside:point withEvent:event];
NSLog(@"A_view--- pointInside withEvent --- isInside:%d",isInside);
return isInside;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"A_touchesBegan");
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
NSLog(@"A_touchesMoved");
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
NSLog(@"A_touchesEnded");
}

第一种不能连续点击我目前的做法通常都是将按钮的可点击置为NO,然后等到网络请求完毕后才将按钮置为YES,不过这么做的缺点也是显而易见的,就是一旦网络速度慢,返回慢的情况,这个按钮就无法点击了,要等很久很久,用户体验不好。网上对于这种解决方式通常给这么以下几点:
1.按钮点击后取消之前的操作,说实话我感觉这种方法就是扯淡,不知道为什么那么多人推荐 官方文档上面cancelPreviousPerformRequestsWithTarget的意思是取消执行先前使用performSelector注册的请求:withObject:afterDelay:。
- (void)buttonClicked:(id)sender{//点击按钮后先取消之前的操作,再进行需要进行的操作
[[selfclass]cancelPreviousPerformRequestsWithTarget:selfselector:@selector(buttonClicked:)object:sender];
[selfperformSelector:@selector(buttonClicked: )withObject:senderafterDelay:0.2f];
}
我的感觉它根本就不取消啥
2.使用Runtime让所有按钮在0.几秒后都不能继续点击。嗯,这种方法,怎么说呢,我也用过,一方面在非常轴的测试面前,这种方式仍然会有几率造成重复点击,另一种方面对于这个需求来说使用runtime这种方式显得有点大才小用了,同时这会造成代码污染,以后出现的bug问题你都不好判断寻找。 所以这种被我pass掉了,不过大神除外哈。
3.这个也就是我目前使用的看起来最土的一种方法,不过有效简单稳定,有更好的方法也请教教我。
同时这段时间工作也接触了ReactNative的东西,RN里button还是叫button,有一些props对于属性而已,上面那段我们提到了runtime,那么下一节我们就说一下runtime。