UI界面学习路线及重难点(UIKit)
为了便于开发者打造各式各样的优秀App,UIKit 框架提供了非常多功能强大又易用的UI控件,如下只列出易忘的部分属性。
- 基础控件:
UIView、UILabel、UIButton、UIImageView等 - 高级控件:
UITableView、UICollectionView - 屏幕适配:
Autolayout、Sizeclass、Masonry - 多控制器:
UINavicationController、UITabBarController、自定义控制器管理 - Quartz2D、核心动画、事件处理、手势、UIDynamiC
基础控件
以下列举一些在开发中可能用得上的UI控件:
UIPageControl、UITextView、UIActivityIndicator、UISwitch、UIDatePicker、UIToolbar、UIProgressView、UISlider、UISegmentControl、UIPickerView、UITextField、WKWebView、UINavigationBar、UITabBar
小知识Tip:
UIBarButtonItem: 描述按钮的具体内容
UINavigationItem: 设置导航条上内容(左边,右边,中间)
UITabBarItem: 设置tabBar上按钮内容(UITabBarButton)
如下包含知识点:
UIView、UILabel、UIButton、UIImageView、图片的加载方式、设置毛玻璃效果、背景图片的拉伸、播放音效
UIView
@property(nonatomic) CGRect frame; // 在父控件中的位置尺寸(以父控件的左上角为坐标原点)
@property(nonatomic) CGRect bounds; // 该控件的位置尺寸(以自己左上角为坐标原点bounds的x,y一般为0)
@property(nonatomic) CGPoint center; // 控件中心点位置(以父控件的左上角为坐标原点)
UILabel
@property(nonatomic) NSLineBreakMode lineBreakMode; // 换行模式
@property(nonatomic) UIColor shadowColor; // 设置阴影所需
@property(nonatomic) CGSize shadowOffset; // 设置阴影所需
UIButton
@property(nonatomic) UIEdgeInsets contentEdgeInsets; // 设置内容 内边距
@property(nonatomic) UIEdgeInsets imageEdgeInsets; // 设置图片 内边距
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // 设置标题 内边距
@property(nonatomic) UIControlContentVerticalAlignment contentVerticalAlignment; // 控制按钮内部的子控件对齐,不是用contentMode,用该属性及contentHorizontalAlignment
UIImageView
@property(nonatomic,copy) NSArray *animationImages; // 显示的动画图片
@property(nonatomic) NSTimeInterval animationDuration; // 动画图片的持续时间
@property(nonatomic) NSInteger animationRepeatCount; // 动画的播放次数。默认0,代表无限播放
- (void)startAnimating; // 开始动画
- (BOOL)isAnimating; // 是否正在执行动画
图片的加载方式
imageNamed:- 就算指向它的指针被销毁,该资源也不会从内存中干掉
- 放到Assets.xcassets的图片,默认就有缓存
- 如果图片较小,并且频繁使用的图片,建议使用
imageWithContentOfFile:- 指向它的指针被销毁,该资源会被从内存中干掉
- 如果图片较大,并且使用次数较少,建议使用
设置毛玻璃效果
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
imageView.image = [UIImage imageNamed:@"image_name"];
imageView.contentMode = UIViewContentModeScaleAspectFill;
// 加毛玻璃效果
UIToolBar *toolBar = [[UIToolBar alloc] initWithFrame:imageView.bounds];
// 设置毛玻璃样式
toolBar.barStyle = UIBarStyleBlack;
[imageView addSubview:toolBar];
背景图片的拉伸
UIImage *image = [UIImage imageNamed:@"chat_send"]; // 创建UIImage对象
CGFloat imageWidth = image.size.width; // 获取image宽度
CGFloat imageHeight = image.size.height; // 获取image高度
/**
* 返回一张受保护且拉伸的图片 ---> CapInsets: 哪些地方要保护
* UIImageResizingModeTile: 平铺
* UIImageResizingModeStretch: 拉伸(伸缩)
*/
UIImage *resizableImage = [image resizableImageWithCapInsets:
UIEdgeInsetsMake(imageHeight*0.5, imageWidth*0.5, imageHeight*0.5-1, imageWidth*0.5-1)
resizingMode:UIImageResizingModeTile];
// 方式二,简洁
// [image stretchableImageWithLeftCapWidth:imageWidth*0.5 topCapHeight:imageHeight*0.5];
[self.button setBackgroundImage:resizableImage forState:UIControlStateNormal];
播放音效
@property (nonatomic, strong) AVPlayer *player; // 播放器
self.player = [[AVPlayer alloc] init]; // 创建播放器
NSURL *url = [[NSBundle mainBundle] URLForResource:@"song.mp3" withExtension:nil];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
[self.player replaceCurrentItemWithPlayerItem:playerItem]; // 切换歌曲
[self.player play]; // 播放
self.player.rate = 2.0; // 调节速率
UI控件的weak和strong,weak和assign
weak
1> OC对象assign
1> 基本数据类型
2> OC对象strong
1> OC对象copy
1> NSString *
2> block- Q:使用
weak和assign修饰OC对象的区别
1> 成员变量
1)weak生成的成员变量是用__weak修饰的,比如Cat * __weak _cat;
2)assign生成的成员变量是用__unsafe_unretained修饰的Cat * __unsafe_unretained _cat;
2>__weak和__unsafe_unretained
1) 都不是强指针(不是强引用),不能保住对象的命
2)__weak:所指向的对象销毁后,会自动变成nil指针(空指针),不再指向已经销毁的对象
3)__unsafe_unretained:所指向的对象销毁后,仍旧指向已经销毁的对象
高级控件
UIScrollView
小知识Tip:
// 设置是否允许自动修改 UIScrollView 内边距
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets; //ios(7.0,11.0)
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior //ios(11.0)
@property(nonatomic) BOOL automaticallyAdjustsScrollIndicatorInsets; //ios(13.0)
@property(nonatomic) BOOL bounces; // 设置UIScrollView是否需要弹簧效果
@property(nonatomic) BOOL alwaysBounceVertical; // 不管是否设置contentSize总是有弹簧效果(下拉刷新)
@property(nonatomic) BOOL pagingEnabled; // 开启分页功能
// UIPageControl
pageControl.numberOfPages; // 设置总页数
pageControl.currentPage = page; // 当前页码
pageControl.hidesForSinglePage = YES; // 单页的时候是否隐藏
// NSTimer
@property(nonatomic, weak) NSTimer *timer;
- (void)startTimer {
// 返回一个自动执行的定时器对象
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextPage:) userInfo:nil repeats:YES];
/**
* 目的:不管主线程在做什么操作,都会分配一定的时间处理定时器
* NSDefaultRunLoopMode(默认):同一时间只能执行一个任务
* NSRunLoopCommonModes(公用):可以分配一定的时间执行其他任务
*/
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)stopTimer {
[self.timer invalidate];
self.timer = nil;
}
UITableView
1、性能优化-cell重用; 2、左滑删除; 3、编辑模式,批量删除
UITableViewCell自动高度
UITableViewCell自动高度卡顿优化
UICollectionView
UICollectionView 基本使用
- 创建
UICollectionViewLayout,使用系统默认流式布局,或自定义布局; - 创建
UICollectionView,设置delegate和datasource,注册cell类型; - 选择实现
UICollectionViewDataSource中方法,行数、cell 复⽤; - 选择实现
UICollectionViewDelegate方法(点击、滚动等);
UICollectionViewFlowLayout 流式布局,每行排满后自动换行; 关键熟悉如下:
//一个section有很多行item,这个属性表示最小行距,默认值不是0(比如第一行和第二行item的行距)
@property (nonatomic) CGFloat minimumLineSpacing;
//这个属性表示两个item之间的最小间距,默认值不是0;(⽤来计算一行可以布局多少个 item)
@property (nonatomic) CGFloat minimumInteritemSpacing;
//这个属性表示section的内边距,上下左右的留边
@property (nonatomic) UIEdgeInsets sectionInset;
屏幕适配
| 手机机型 (iPhone) | 设备分辨率 (px) | 逻辑分辨率 (pt) - (ppi) | 屏幕尺寸英寸 (inch) | 像素图缩放因子 (Scale Factor) |
|---|---|---|---|---|
| 2023 ↑ | 2023 ↑ | 2023 ↑ | 2023 ↑ | 2023 ↑ |
| iPhone14 Pro Max | 1290x2796 | 430x932 - 460 | 6.7 | @3x |
| iPhone14 Pro | 1179x2556 | 393x852 - 460 | 6.1 | @3x |
| 12/13 ProMax、14Plus | 1284x2778 | 428x926 - 458 | 6.7 | @3x |
| 12/13/14、12/13 Pro | 1170x2532 | 390x844 - 460 | 6.1 | @3x |
| 12mini/13mini | 1080x2340 | 360x780 - 476 | 5.4 | @3x |
| Xs Max/11 ProMax | 1242x2688 | 414x896 - 458 | 6.5 | @3x |
| XR/11 | 828x1796 | 414x896 - 326 | 6.1 | @2x |
| X/Xs/11Pro | 1125x2436 | 375x812 - 458 | 5.8 | @3x |
| 6p/6sp/7p/8p Plus | 1242x2208 | 414x736 - 401 | 5.5 | @3x |
| 6/6s/7/8/SE2/SE3 | 750x1334 | 375x667 - 326 | 4.7 | @2x |
| iPhone5/5s/5c/SE1 | 640x1136 | 320x568 - 326 | 4 | @2x |
| iPhone4/4s | 640x960 | 320x480 - 326 | 3.5 | @1x |
| iPhone2G/3G/3GS | 320x480 | 320x480 - 163 | 3.5 | @1x |
iPhone设备默认指令集
Autolayout
核心计算公式:obj1.property1 = (obj2.property2 * multiplier) + constant value
- 约束:通过给控件添加约束,来决定控件的位置和尺寸
- 参照:添加约束时,依照谁来添加(可以是父控件或兄弟控件)
添加约束的规则
- 在创建约束之后,需要将其添加到作用的view上
对于两个同层级view之间的约束关系,添加到它们的父view上对于两个不同层级view之间的约束关系,添加到它们最近的共同父view上
// 例如:
tempView.translatesAutoresizingMaskIntoConstraints = NO; // 禁止autoresizing自动转为约束
// 宽度约束-这里只列出一项
NSLayoutConstraint *wlcs = [NSLayoutConstraint constraintWithItem:tempView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:100];
[tempView addConstraint:wlcs];
使用VFL语言,可视化格式语言
H:[cancelButton(72)]-12-[acceptButton(50)]cancelButton宽72,acceptButton宽50,它们之间间距12
H:[wideView(>=60@700)]wideView宽度大于等于60,该约束条件优先级为700
V:[redBox][yellowBox(==redBox)]- 竖直方向上,先有一个
redBox,其下方紧接一个高度等于redBox高度的yellowBox
- 竖直方向上,先有一个
H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|- 水平方向上,
Find距离父view左边缘默认间隔宽度,之后FindNext距离Find间隔默认宽度,再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间隔都是默认宽度。(竖线表示supperview的边缘)
- 水平方向上,
// 例如:
// 水平方向
NSDictionary *views = NSDictionaryOfVariableBindings(redView, blueView);
NSDictionary *metrics = @{@"space" : @30};
NSString *hvfl = @"H:|-space-[blueView]-space-[redView(==blueView)]-space-|"
NSArray *hlcs = [NSLayoutConstraint constraintsWithVisualFormat:hvfl options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:metrics views:views];
[self.view addConstraints:hlcs];
// 垂直方向
NSString *vvfl = @"V:[blueView(50)]-space-|";
NSArray *vlcs = [NSLayoutConstraint constraintsWithVisualFormat:vvfl options:KnilOptions metrics:metrics views:views];
[self.view addConstraints:vlcs];
Sizeclass
TODO
Masonry
https://github.com/SnapKit/Masonry
WebView
- WebKit 框架
- WebKit 是一个开源的 Web 浏览器引擎。
- WebKit.framework 就是在 WebCore、底层桥接、JSCore 引擎等核心模块的基础上,针对iOS平台的项目封装。
- 基本的加载
- 通过 configuration 进行基本设置(例: 基本的共享 Cookie 设置、基础偏好设置、播放视频设置、默认 JS 注入)
- 加载 URL & HTML
- 类比之前的 UIKit 提供基础的功能,在 delegate 中处理业务逻辑
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration;
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
WKWebView Delegates
-
WKNavigationDelegate
decidePolicyForNavigationAction是否加载请求 (scheme 拦截、特殊逻辑、JS 和 Native 通信)didFinishNavigationwebView 完成加载(业务逻辑)didFailNavigationwebView 加载失败 (loadingView展示,重试按钮等)webViewWebContentProcessDidTerminatewebView Crash 回调 (⾃自动重新加载)
-
WKUIDelegate
runJavaScriptAlertPanelWithMessagerunJavaScriptConfirmPanelWithMessagerunJavaScriptTextInputPanelWithPrompt处理理 alert( ) / confirm( ) / prompt( ) 自定义样式
UIApplication
UIApplication对象是应用程序的象征,每一个应用都有自己的UIApplication对象,而且是单例的,一个iOS程序启动后创建的第一个对象就是UIApplication对象,利用UIApplication对象,能进行一些应用级的操作。如下列出常用属性:
@property(nonatomic) NSInteger applicationIconBadgeNumber; //设置程序图标右上角的红色提醒数字@property(nonatomic) BOOL networkActivityIndicatorVisible; //设置联网指示器的可见性- 以及统一设置状态栏,也可由UIViewController管理,如下
- (UIStatusBarStyle)preferredStatusBarStyle; //状态栏样式- (BOOL)prefersStatusBarHidden; //状态栏的可见性
UIApplicationDelegate
iOS程序的启动过程:
application:didFinishLaunchingWithOptions:程序加载完毕applicationDidBecomeActive:程序获取焦点applicationDidEnterBackground:程序进入后台applicationWillResignActive:程序失去焦点applicationWillEnterForeground:程序从后台回到前台applicationDidReceiveMemoryWarning:内存警告,可能要终止程序applicationWillTerminate:程序即将退出
控制器管理
UINavicationController
TODO
UITabBarController
TODO
Quartz2D、核心动画、事件处理、手势、UIDynamiC
transform
// transform 形变 - 平移操作
self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, 0, 100); // 相对上一次做形变
// transform 形变 - 旋转操作
self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, M_PI_4);
// transform 形变 - 缩放操作
self.imageV.transform = CGAffineTransformScale(self.imageV.transform, 0.8, 0.8);
事件
/**
* iOS中事件可分为3大类型
* 1.触摸事件
* 2.加速计事件
* 3.远程控制事件
* 只有继承了UIResponder对象才能接收并处理事件,我们称之为“响应者对象”;UIResponder提供了如下方法:
*/
// 触摸事件
- (void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event; // 开始触摸view
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; // 手指在view上移动
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; // 手指离开view
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; // 触摸结束前,某个系统事件(电话呼入)打断
// 加速计事件
- (void)motionBegin:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
// 远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
UITouch
@property (nonatomic, readonly, retain) UIWindow *window; // 触摸产生时所处的窗口
@property (nonatomic, readonly, retain) UIView *view; // 触摸产生时所处的视图
@property (nonatomic, readonly) NSUInteger tapCount; // 短时间内点按屏幕的次数,可根据tapCount判断单击、双击等
@property (nonatomic, readonly) NSTimeInterval timestamp; // 记录了触摸事件产生或变化时的时间,单位是秒
- (CGPoint)locationInView:(UIView *)view; // 返回值表示触摸在view上的位置
- (CGPoint)previousLocationView:(UIView *)view; // 该方法记录了前一个触摸点的位置
事件的产生和传递
- 发生触摸事件后,系统会将该事件加入到一个由
UIApplicationMain管理的事件队列中 UIApplicationMain会从事件队列中取出最前面的事件,并将事件分发下去处理,通常先发送给主窗口(keyWindow)- 主窗口会在视图层次结构中
找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步 - 找到合适的视图控件后,就会调用视图控件的touches方法来做具体的事件处理
// 作用:去寻找最适合的view; 何时调用:当一个事件传递给当前view时调用
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
}
/**
作用:判断当前点在不在它调用view(谁调用pointInside,这个view就是谁)
何时调用:它是在hitTest方法中调用的
*/
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
}
事件传递的完整过程
- 先将事件对象由上往下传递
(由父控件传递给子控件),找到最合适的控件来处理这个事件 - 调用最合适控件的
touches...方法 - 如果调用了
[supper touches...]就会将事件顺着响应者链条往上传递,传递给上一个响应者 - 接着就会调用上一个响应者的
touches...方法
手势
UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,使用它的子类才能处理具体手势UITapGestureRecognizer 敲击UIPinchGestureRecognizer 捏合,用于缩放UIPanGestureRecognizer 拖拽UISwipeGestureRecognizer 轻扫UIRotationGestureRecognizer 旋转UILongGestureRecognizer 长按
Quartz2D
Quartz2D是一个二维绘图引擎,同时支持iOS和Mac系统,能完成如下工作:
- 绘制图形 (线条/三角形/矩形/圆/弧)等
- 绘制文字
- 绘制/生成图片(图像)
- 读取/生成PDF
- 截图/裁剪图片
- 自定义UI控件(涂鸦/画板)
- ……
** 图形上下文(Graphics Context) 类型为CGContextRef **
- 作用
- 保存绘图信息,绘图状态
- 决定绘制的输出目标(绘制到什么地方去?输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
- 相同的一套绘图序列,指定不同的图形上下文,就可将相同的图像绘制到不同的目标
- Quartz2D提供了以下几种类型的Graphics Context
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context
- Printer Graphics Context
案例-自定义view
- 新建类继承UIView,实现
- (void)drawRect:(CGRect)rect方法,在该方法中- 取得跟当前view相关联的图形上下文
- 绘制相应的图形内容
- 利用图形上下文将绘制的所有内容渲染显示到view上面
小知识Tip:
- 为什么要实现drawRect:方法才能绘制到view上?
- 因为在drawRect:方法中才能取得跟view相关联的图形上下文
- drawRect:方法在什么时候被调用?
- 当view第一次显示到屏幕时(被加到UIWindow上显示出来)
- 调用view的setNeedsDisplay或者setNeedsDisplayInRect:时
高阶动画
TODO