URL
URL的组成部分只能使用:
- 26个英语字母(包括大写和小写)
 - 10个阿拉伯数字
 - 连词号(-)
 - 句点(.)
 - 下划线(_)
 
此外还有18个保留字,只能在指定的位置出现,如果要在其他位置出现,就必须要对齐进行编码:
!:%21#:%23 锚点$:%24&:%26 分隔多个查询参数':%27(:%28):%29*:%2A+:%2B,:%2C/:%2F::%3A 分隔协议和主机;:%3B=:%3D?:%3F 分隔路径和查询参数@:%40[:%5B]:%5D
URL编码是ASCII编码,而不是Unicode。
- ASCII的局限在于只能显示26个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语
 
URL格式
1
https://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#anchor
协议(scheme)
https://浏览器请求服务器资源的方法
主机\域名(host)
www.example.com资源所在的网站名或服务器的名字
端口(port)
80(默认为80)同一个域名下面可能同时包含多个网站,它们之间通过端口区分
路径(path)
/path/index.html资源在网站的位置
查询参数(parameter)
?key1=value1&key2=value提供给服务器的额外信息。参数的位置是在路径后面,两者之间使用
?分隔。查询参数可以有一组或多组。
 每组参数以
key=value的形式表示。 多组参数之间使用
&连接。锚点(anchor)
#anchor网页内部的定位点。锚点名称通过网页元素的
id属性命名
Calendar
1  | Calendar(identifier: .gregorian) // 公历,最常使用  | 
1  | //2000-01-1 00:00:00  | 
calendar.range( of: .day, in: .month, for: baseDate)?.count- baseDate所在月份的总天数
 
calendar.date(from: calendar.dateComponents([.year, .month], from: baseDate))- baseDate所在月的第一天
 
calendar.component(.weekday, from: firstDayOfMonth)- firstDayOfMonth所在一周的第几天
 
UIStackView的inset
1  | stackView.isLayoutMarginsRelativeArrangement = true  | 
自定义UIStackView的spacing
1  | 嵌套stackView //iOS11之前  | 
一个string是否包含另外一个string
1  | contains(_:)  | 
两个string是否相等
1  | ==  | 
pod版本限制
pod 'FLEX', '~> 2.2.1':版本会维持在2.2.X的最大版本(2.2.99)
1  | logical operators:  | 
绘制圆角矩形
1  | let placeholderImage = UIGraphicsImageRenderer(bounds: CGRect(origin: .zero, size: CGSize(widthHeight: 50))).image { context in  | 
在viewDidDisappear的时候可以判断是否是被pop
若navigationController==nil,说明被pop掉
绘制image时,指定的frame和生成的image会有误差
systemLayoutSizeFitting
在调用systemLayoutSizeFitting计算view的高度之前需要先指定view中label的preferredMaxLayoutWidth
UIStackView
AutoLayout
- stackView会自动管理布局
 
属性
Distribution:延轴方向分布
- 
- 若stackView大小确定:子view会被拉伸或压缩来填充整个stackView
 - 若stackView大小不确定:需要每个子view大小确定,stackView会调整自身大小以适应子view
 
 - 
- 所有子view一样大
 
 - 
- 根据子view的intrinsic content size的比例调整大小,以适应stackView
 
 - 
- 每个子view的间距相等
 - 当子view会超出stackView时,会根据子view的 compression resistance priority优先级压缩
 
 - 
- 每个子view的中心距离相等
 - 当子view会超出stackView时,会根据子view的 compression resistance priority优先级压缩
 
 
Alignment:垂直轴方向分布
isLayoutMarginsRelativeArrangement
设置边距
1
2stackView.isLayoutMarginsRelativeArrangement = true
stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)优点:
简单
可以设置动画
1
2
3
4stackView.directionalLayoutMargins.leading = 0
UIView.animate(withDuration: 0.3) {
stackView.layoutIfNeeded()
}
setCustomSpacing(_:after:)
自定义间距
1
2
3
4vStackView.addArrangedSubview(movieLabel)
vStackView.addArrangedSubview(quoteLabel)
vStackView.setCustomSpacing(10, after: quoteLabel) // <1>
vStackView.addArrangedSubview(authorLabel)iOS11之后才能用,在11之前只能用嵌套stackView
只有在将quoteLabel加入stackView之后再设置间距才会生效
若将quoteLabel设为隐藏,它后面的间距也会隐藏
设备及其在竖/横屏下的宽高是regular还是compact
| Device | Portrait orientation | Landscape orientation | 
|---|---|---|
| 12.9” iPad Pro | Regular width, regular height | Regular width, regular height | 
| 11” iPad Pro | Regular width, regular height | Regular width, regular height | 
| 10.5” iPad Pro | Regular width, regular height | Regular width, regular height | 
| 9.7” iPad | Regular width, regular height | Regular width, regular height | 
| 7.9” iPad mini 4 | Regular width, regular height | Regular width, regular height | 
| iPhone XS Max | Compact width, regular height | Regular width, compact height | 
| iPhone XS | Compact width, regular height | Compact width, compact height | 
| iPhone XR | Compact width, regular height | Regular width, compact height | 
| iPhone X | Compact width, regular height | Compact width, compact height | 
| iPhone 8 Plus | Compact width, regular height | Regular width, compact height | 
| iPhone 8 | Compact width, regular height | Compact width, compact height | 
| iPhone 7 Plus | Compact width, regular height | Regular width, compact height | 
| iPhone 7 | Compact width, regular height | Compact width, compact height | 
| iPhone 6s Plus | Compact width, regular height | Regular width, compact height | 
| iPhone 6s | Compact width, regular height | Compact width, compact height | 
| iPhone SE | Compact width, regular height | Compact width, compact height | 
UIScrollView
contentSize
scrollView内容的大小
contentOffset
contentSize的origin 和 frame的origin的偏移
当scrollview向下拉时,offsetY为负数;当上拉时,offsetY不断增大,当越过原点后会变成正数。
contentInset
contentview相对于scrollview的边距
contentInset和adjustedContentInset
iOS11提出了safeArea的概念,帮助scrollView放在屏幕的可视部分
在iOS 11中决定tableView的内容与边缘距离的是adjustedContentInset属性,而不是contentInset。
contentsize是scrollView的滚动范围
contentinset是为scrollView增加额外的滚动区域
contentoffset是scrollView的滚动位置
safeAreaInsets是view到safeArea的距离
adjustedContentInset表示contentView.frame.origin偏移了scrollview.frame.origin多少。是系统计算得来的,计算方式由
contentInsetAdjustmentBehavior决定。有以下几种计算方式:ScrollableAxes
- 在可滚动方向上:adjustedContentInset = safeAreaInset + contentInset,
 - 在不可滚动方向上:adjustedContentInset = contentInset;
 
Automatic:
scrollview在一个automaticallyAdjustsScrollViewInsets = YES的controller上,并且这个Controller包含在一个navigation controller中:
在top & bottom上的 adjustedContentInset = safeAreaInset + contentInset,不管是否滚动。
其他情况下:
与
ScrollableAxes相同
Never
- adjustedContentInset = contentInset
 
Always
- adjustedContentInset = safeAreaInset + contentInset
 
ContentInsetAdjustmentBehavior
如上
automaticallyAdjustsScrollIndicatorInsets
是否自动调整滚动条的insets
translatesAutoresizingMaskIntoConstraints
默认值是true
若值是true时,系统会自动创建和view的 autoresizing mask完全相同的约束,并且这些约束包含了view的size、position。因此此时在添加额外的约束时,会有布局冲突。
所以在使用Auto Layout来进行动态布局时,需要将该属性设为false。
note:在使用snapKit布局时,snapkit会自动将该值设为false。但是若没有对一个view使用snp布局,该值仍然保持为true。
setContentCompressionResistancePriority &setContentHuggingPriority
- setContentCompressionResistancePriority:抗压缩性,适用于布局空间比view的size要小
 - setContentHuggingPriority: 抗拉伸性,适用于布局空间比view的size要大
 
监听键盘事件
1  | public class let keyboardWillShowNotification: NSNotification.Name  | 
获取键盘信息
1  | public class let keyboardFrameBeginUserInfoKey: String //键盘动画起始时的frame  | 
示例
1  | NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)  | 
UIModalPresentationStyle和UIModalTransitionStyle
1  | enum UIModalPresentationStyle : Int {  | 
1  | enum UIModalTransitionStyle : Int {  | 
用UIViewControllerTransitioningDelegate显示蒙层
1  | extension VC: UIViewControllerTransitioningDelegate {  | 
内存泄漏
自己监听自己
self.rx.observe(CGRect.self, "bounds").map({ $0 ?? .zero }).distinctUntilChanged()解决方法:
contentView.rx.observe(CGRect.self, "bounds").map({ $0 ?? .zero }).distinctUntilChanged()viewModel中的监听被view的bag管理而不是reuseBag
viewModel.relayObject.oberve()....disposed(by bag)解决方法:
viewModel.relayObject.oberve()....disposed(by reuseBag)
监听View大小
bounds
scrollView.rx.observe(CGRect.self, "bounds").map({ $0 ?? .zero }).distinctUntilChanged()
scrolView的contentSize
scrollView.rx.observe(CGSize.self, "contentSize").map({ $0 ?? .zero }).distinctUntilChanged()
音频
AVQueuePlayer
可以自动管理多个网络音频
AVQueuePlayer在播放完成之后会把palyItem从队列中删除,因此在播放时要判断playItem是否为空,
若是空需要调整playItem的播放进度,然后将item重新加入queue中
1  | if player.items().isEmpty {  | 
监听加载状态
1  | // AVPlayerItem状态改变  | 
监听播放完成(播放完成,重新将进度设为第一帧)
1  | // 播放完成  | 
监听播放失败
1  | // 播放失败  | 
在OC和swift中的Int64类型转换
swift中的int传给OC后,OC会将其转为NSNumber类型,在OC中若想使用int类型,就需要调用NSNumber的方法将其转为Int类型
代码注意
- 分母的变量要用
.clamped(to: 1.0...)保证其值大于1 - static的全局变量会自动转成lazy
 - 将计算属性
var a { ... }转为var a = { ... }()可使得闭包内代码只运行一次 
会导致source kit崩溃的原因
rx.tapGuesture用在非UIButton上
移除当前显示的controller
1  | strongSelf.dismiss(animated: true, completion: nil)  | 
1  | current.navigationController?.viewControllers = current.navigationController?.viewControllers.filter {  | 
dismiss
func dismiss(animated: Bool, completion: (() -> Void)?)
Dismisses the view controller that was presented modally by the view controller.
A -> B -> C
C.dismiss : 移除C
B.dismiss : 移除C
A.dismiss : 移除B,C
PresentedViewController 与 PresentingViewController
假设Controller A通过present跳到Controller B,B又通过present跳到Controller C,那么:
B.presentedViewController 就是 C
B.presentingViewController 就是 A
iOS权限查询
1  | func permissions() {  | 
通知权限
1  | UNUserNotificationCenter.current().getNotificationSettings { [weak self] (settings) in  | 
保证有相机权限
1  | _ = AVCaptureDevice.rx.cameraAuthorizedOrShowAlertIfNeeded  | 
跳到设置界面
1  | if let url = URL(string: UIApplication.openSettingsURLString) {  | 
API获取的整数转为枚举
1  | enum Grade: Int {  | 
根据不同的评级使用不同的图片
1  | extension Grade {  | 
图片旋转
仿射变换:是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。(来自百度百科)
在iOS中表现为平移、旋转、缩放等。
1  | iamgeView.transfrom = CGAffineTransform(rotationAngle: CGFloat.pi * (-15.0 / 180.0))  | 
根据不同的情景使用不同的布局
一、布局的offset不同
1  | import SnapKit  | 
二、布局的参考对象不同
1  | import SnapKit  | 
rx只监听一次
1  | .take(1)  | 
定时器
1  | [self.timer setFireDate:[NSDate distantPast]] //开始计时器  | 
1  | final class TimerHelper {  | 
从gerrit上download已经revert的提交
复制Cherry Pick URL,在本地分支运行,
若有冲突,解决冲突后,commit(message直接复制被revert的信息,包含changeID)
名词
keypoint:知识点question:题目
时间
1  | let timeString: String = DateUtils.dateString(from: .init(timeIntervalSince1970: someTime), withDateFormat: "yyyy年MM月dd日HH时mm分")  | 
把一个view包装为左侧有2pixl的新view
1  | view.leo.wrapped(left: 2.0)  | 
获取API错误返回的状态码
1  | UpdateUserInfoAPI.rx.response()  | 
frog
1  | ProfileFrogConstants.eventCameraPermission.leo.frog(parameters: ["permission": "true"])  | 
判断是否是表情
1  | private extension String {  | 
判断字符是否是汉字
1  | if character >= "\u{4e00}" && character <= "\u{9fa5}" {  | 
日期扩展
1  | extension Date {  | 
富文本
1  | let price = NSMutableAttributedString(  | 
计算文字高度
boundingRect
1  | let size = labelText.boundingRect(  | 
1  | extension String {  | 
systemLayoutSizeFitting
1  | func layoutSize(in context: layoutContext) -> layoutSize {  | 
对齐设置
方法一:使用snp
1  | buttonC.snp.makeConstraints { make in  | 
方法二:直接设置
1  | buttonC.translatesAutoresizingMaskIntoConstraints = false  | 
设置根视图
在SceneDelegate.swift文件中的scene()函数中添加
1  | let nav = UINavigationController(rootViewController: ViewController())  | 
Podfile使用
- 安装cocoapod
 - 在项目目录下创建
Podfile文件 - 在文件中添加响应的依赖
 - 允许
pod install 
分割线
1  | private let line = UIView() //存储属性  | 
或者使用SeparatorCollectionViewLayout(),在init()中传递给父类init()方法
监听某个存储属性
给属性增加
@ObservableProperty在界面中使用
instance.$property.subscribe来监听1
2
3
4
5
6
7MyLogger.shared.$messages
.observeOn(MainScheduler.instance) //把线程切换到主线程
.subscribe(onNext: { [weak self] in
guard let strongSelf = self else { return }
//通过$0获取监听的属性
})
.disposed(by: bag)
也可以给属性包装为BehaviorRelay,用.accept添加值并通知监听者,用rx的.subscribe来处理响应
viewDidLoad的功能切分
- setupSubviews()
 - setupLayout()
 - setupActions()
 
点击背景隐藏键盘
1  | collectionView.keyboardDismissMode = .onDrag  | 
方法二:
创建一个手势 private let tapBackground = UITapGestureRecognizer()
将手势添加到view中:view.addGestureRecognizer(tapBackground)
设置手势点击完成编辑:
1
2
3
4
5
6tapBackground.rx.event
.subscribe(onNext: { [weak self] _ in
guard let strongSelf = self else { return }
strongSelf.view.endEditing(true)
})
.disposed(by: bag)
搜索框
用textField
1  | private let textField = UITextField()  |