一、学习笔记
-
根据十六进制的色号来设置颜色:
-
使用Category,为UIColor类添加将解析十六进制色号为RGB值的方法
-
UIColor+Hex.h
#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface UIColor (Hex) // 默认alpha为1 + (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue; // 从十六进制字符串获取颜色,默认alpha为1 color支持@“#123456”, @“0X123456”, @“123456”三种格式 + (UIColor *)colorWithHexString:(NSString *)color; // 从十六进制字符串获取颜色,alpha需要自己传递 color支持@“#123456”, @“0X123456”, @“123456”三种格式 + (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha; @end NS_ASSUME_NONNULL_END
-
UIColor+Hex.m
#import "UIColor+Hex.h" @implementation UIColor (Hex) + (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue { return [UIColor colorWithRed:red green:green blue:blue alpha:1]; } + (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha { // 删除字符串中的空格 NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString]; // 得到的字符串的长度应该是6或者8 if ([cString length] < 6) { return [UIColor clearColor]; } // 如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾 if ([cString hasPrefix:@"0X"]) { cString = [cString substringFromIndex:2]; } // 如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾 if ([cString hasPrefix:@"#"]) { cString = [cString substringFromIndex:1]; } if ([cString length] != 6) { return [UIColor clearColor]; } // 将字符串分为r,g,b三个分量 NSRange range; range.location = 0; range.length = 2; NSString *rString = [cString substringWithRange:range]; range.location = 2; NSString *gString = [cString substringWithRange:range]; range.location = 4; NSString *bString = [cString substringWithRange:range]; // 将字符串十六进制两位数字转为十进制整数 unsigned int r, g, b; [[NSScanner scannerWithString:rString] scanHexInt:&r]; [[NSScanner scannerWithString:gString] scanHexInt:&g]; [[NSScanner scannerWithString:bString] scanHexInt:&b]; // 返回对应的颜色对象 return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha]; } // 默认alpha值为1 + (UIColor *)colorWithHexString:(NSString *)color { return [self colorWithHexString:color alpha:1.0f]; } @end
-
-
OC中继承、分类、扩展的对比:
-
继承:
-
OC中的继承与C++中的一样,实现继承的子类将拥有父类所有的属性和方法,子类可以重写父类的方法,可以通过
super
调用父类的方法;但是与C++不同的是,OC中只能单继承
-
OC中的继承与C++中的一样,实现继承的子类将拥有父类所有的属性和方法,子类可以重写父类的方法,可以通过
-
分类 Category:
-
分类是OC特有的属性,利用OC的动态运行时分配机制,在现有类的基础上添加新的方法,也可以添加成员变量(不能是原有类的成员变量),但是添加的成员变量不会自动生成
setter
和
getter
方法,需要在
@implement
中自己实现 - 不能在分类中定义与原有类同名方法,不同的分类之间也不可以有同名的方法,如果在分类中增加了一个与原有类同名的方法,那么分类中的方法会覆盖原有类的方法,如果多个分类中有相同的方法,执行最后编译的方法
-
分类是OC特有的属性,利用OC的动态运行时分配机制,在现有类的基础上添加新的方法,也可以添加成员变量(不能是原有类的成员变量),但是添加的成员变量不会自动生成
-
拓展 Extension:
-
拓展就是匿名的分类,只有
.h
文件没有
.m
文件,原类名称后面的括号中没有东西,只能扩展方法,不能添加成员变量,类扩展得到的属性和方法都是私有的,扩展的方法只能在原类中实现,外部无法调用
-
拓展就是匿名的分类,只有
-
继承:
-
成员变量和属性的区别:
@interface Person : NSObject { NSString *sex; // 成员变量 } @property (nonatomic, copy) NSString *name; // 属性 @end @implementation Person { @public NSInteger *age; // 成员变量 }
-
成员变量:(实际开发中很少使用)
-
成员变量能声明作用范围,各修饰符的作用范围如下:
- @public:在任何地方都能直接访问对象的成员变量
-
@private:只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的
getter/setter
方法 - @protected:可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的)
- @package:在同一个包下就可以直接访问,比如说在同一个框架
-
如果没有显式声明,则在
.m
中的成员变量默认为
private
,在
.h
中的成员变量默认为
protected
-
无论父类是在
@interface
还是
@implementation
声明的成员变量子类都能拥有,但是子类能不能直接通过变量名来访问父类中定义的成员变量是需要看父类中定义的成员变量是由什么修饰符来修饰的 -
成员变量不会自动生成
getter/setter
方法,需要自己手动实现 -
成员变量不能用点语法调用,因为没有
getter/setter
方法,只能使用
self->
调用
-
成员变量能声明作用范围,各修饰符的作用范围如下:
-
属性:
-
属性的默认修饰是
@protected
-
属性会自动生成
getter/setter
方法 -
属性用点语法调用,点语法实际上调用的是
getter/setter
方法
-
属性的默认修饰是
-
成员变量:(实际开发中很少使用)
-
将视图移到界面最前方:
-
调用父视图的
bringSubviewToFont
方法(将视图移到最后面则调用父视图的
sendSubviewToBack
方法) -
[[[UIApplication sharedApplication] keyWindow] addSubview:view];
-
调用父视图的
-
OC函数前面加
-
和
+
的区别:-
+
修饰的方法称为类方法,可以通过类名直接调用(类似于JAVA中的static方法) -
-
修饰的方法称为对象方法,只能实例化一个对象然后通过该对象来调用
-
-
iOS中触摸事件传递机制:
-
只有继承自
UIResponder
的类才能接收并处理触摸事件,包括
UIApplication
、
UIViewController
和
UIView
,
UIResponder
中提供了以下四个对象方法来处理触摸事件- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
-
事件处理的整个流程总结:
-
触摸屏幕产生触摸事件后,触摸事件会被添加到由
UIApplication
管理的事件队列中(即首先接收到事件的是
UIApplication
) -
UIApplication
会从事件队列中取出最前面的事件,把事件传递给应用程序的主窗口
keyWindow
- 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件
-
最合适的
view
会调用自己的
touches
方法处理事件 -
touches
默认做法是把事件顺着响应者链条向上抛
-
触摸屏幕产生触摸事件后,触摸事件会被添加到由
-
二、遇到的问题及解决方法
-
实现了点击某个按钮显示下拉菜单之后,如何实现点击其他位置关闭该下拉菜单:
-
核心思路是在
touchesBegan
中判断触摸点是否属于下拉菜单,如果不属于就隐藏下拉菜单- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { CGPoint point = [[touches anyObject] locationInView:self]; point = [self.alertView.layer convertPoint:point fromLayer:self.layer]; if (![self.alertView.layer containsPoint:point]) { self.hidden = YES; } }
但是这个方法存在一个问题,因为页面中有
UISearchBarController
、
UIScrollViewController
、
UITableViewController
,这些
ViewController
不会把触摸事件传递给父控件,有两种解决方法:-
方法一:重写
UISearchBarController
、
UIScrollViewController
、
UITableViewController
的
touchesBegan
方法,将触摸事件传递到最底层的
UIView
-
方法二:(这种方法是我自己想到的,不知道会不会有什么问题)因为显示下拉菜单后,点击其他位置的效果都应该只能起隐藏下拉菜单的效果,而
UISearchBarController
、
UIScrollViewController
、
UITableViewController
不应该响应,所以可以在下拉菜单下面加一层透明的
UIView
,这样下拉菜单显示时点击其他位置都会触发该
View
的
touchesBegan
方法,隐藏下拉菜单后再将这层
UIView
也隐藏即可
-
方法一:重写
-
三、参考链接
- 根据十六进制的色号来设置颜色
- OC函数前面加’-‘和’+’的区别
- 下拉菜单
- 点击其他位置隐藏下拉菜单
- 点击事件响应链
- 将视图移到屏幕最前方和最后方
- 成员变量和属性的区别
- 继承、分类、拓展的区别