毛玻璃 + runtime全局pop + 面向协议编程 + 粒子动画
组合第一天封装CLOPageView
和瀑布流布局
-
UIVisualEffectView
视图实现毛玻璃效果
// 添加待模糊的图片视图
let imageView = UIImageView(image: UIImage(named: "timg"))
imageView.frame = view.frame
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
// 生成特定样式的模糊效果
let blur = UIBlurEffect(style: .light)
// 根据模糊效果生成模糊视图
let blurView = UIVisualEffectView(effect: blur)
// 设置模糊区域大小
blurView.frame = view.frame
view.addSubview(blurView)
-
UIStackView
实现并列布局, 可做工具栏。UIStackView
的核心便是方便垂直或水平排布多个subview
// 给 5 个 button 设置不同的 tag 同时连线下面的方法
@IBAction func menuClick(_ sender: UIButton) {
switch sender.tag {
case 0:
print("点击了功能1")
case 1:
print("点击了功能2")
case 2:
print("点击了功能3")
case 3:
print("点击了功能4")
case 4:
print("点击了功能5")
default:
fatalError("未处理按钮")
}
}
-
autoresizing
是UIView
的属性,一直存在,使用也比较简单,但是没有autolayout
那样强大。如果你的界面比较简单,要求的细节没有那么高,那么你完全可以使用autoresizing
去进行自动布局
/// 属性说明
//不会随父视图的改变而改变
UIViewAutoresizingNone = 0,
//自动调整view与父视图左边距,以保证右边距不变
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
//自动调整view的宽度,保证左边距和右边距不变
UIViewAutoresizingFlexibleWidth = 1 << 1,
//自动调整view与父视图右边距,以保证左边距不变
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
//自动调整view与父视图上边距,以保证下边距不变
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
//自动调整view的高度,以保证上边距和下边距不变
UIViewAutoresizingFlexibleHeight = 1 << 4,
//自动调整view与父视图的下边距,以保证上边距不变
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
-
swift
中数组常用的两个方法
map
函数能够被数组调用,它接受一个闭包作为参数,作用于数组中的每个元素。闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组
// 可以看到我们甚至可以不再定义可变的数组直接用不可变的就可以
let numbers = [1,2,3]
let sumNumbers = numbers.map { (number: Int) -> Int in
return number + number
}
// 最终简化写法
let sumNumbers3 = numbers.map { $0 + $0 }
// [2,4,6]
filter
可以取出数组中符合条件的元素 重新组成一个新的数组
let numbers = [1,2,3,4,5,6]
let evens = numbers.filter { $0 % 2 == 0 }
// [2,4,6]
-
swift 4.x
中字典转模型问题,要在属性前面加@objc
-
swift
常用的第三方框架
Alamofire
相当于OC
的AFNetworking
Kingfisher
相当于OC
的SDWebImage
用runtime
实现全局pop
用
runtime
的方法打印interactivePopGestureRecognizer
隐藏属性 找出添加手势所需的target
和action
var count:UInt32 = 0
let ivars = class_copyIvarList(UIGestureRecognizer.self, &count)!
for i in 0..<count {
//拿到ivar指针
let nameP = ivar_getName(ivars[Int(i)])
//根据指针找到对应的属性的字符串
let name = String(cString: nameP!)
print(name)
}
// _gestureFlags
// _targets
// _delayedTouches
// _delayedPresses
// _view
guard let targets = interactivePopGestureRecognizer?.value(forKey: "_targets") as? [NSObject] else { return }
print(targets)
// Optional([(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7ff117701e10>)])
let targetObjc = targets[0]
let target = targetObjc.value(forKey: "target")
let action = Selector(("handleNavigationTransition:"))
let panGes = UIPanGestureRecognizer(target: target, action: action)
view.addGestureRecognizer(panGes)
面向协议编程
面向对象or面向协议?
- 传统的面向对象思维方式:
有一只狗,有自己的名字, 可以跑
有一只猫,有自己的名字, 可以跑
有一个人,有自己的名字, 可以跑
抽象出一个动物类, 之后, 将跑的行为放入动物类中
如果需要一个吃的行为, 也可以抽象在动物类中
- 面向对象开发弊端
如果有一个机器人(一辆车), 也有跑的行为, 这个时候如何抽象呢, 显然该抽象方式并不是特别合理
- 真实开发中的场景
对UIViewController/UITableViewController/UICollectionViewController抽象相同的方法
- 面向对象开发核心是: 封装-继承-(多态)
面向协议示例
-
Swift 标准库中有 50 多个复杂不一的协议,几乎所有的实际类型都是满足若干协议的
-
面向协议开发核心是: 模块化(组件化)
-
面向协议开发应用: 将功能抽取在单独的协议中, 如果有控制器需要, 实现我的协议即可
/* 命名一般以 able 结尾
swift 可以在协议中实现方法 */
// MARK: - 方法声明
protocol NibLoadable {
}
// MARK: - 方法实现
/* 方法实现只能写在 extension 中
用协议封装 加载 xib 的方法
用 where 给协议方法增加条件约束 只有 UIView 才能调用
协议方法只能用 static 修饰 */
extension NibLoadable where Self: UIView {
static func loadFromNib(_ nibName: String? = nil) -> Self {
let loadName = nibName == nil ? "\(self)" : nibName!
return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
}
}
在
TestView
中遵循并实现协议TestView.loadFromNib()
粒子动画
func emitterAnimationStart(_ point: CGPoint) {
// 1.创建发射器
let emitter = CAEmitterLayer()
// 2.设置发射器的位置
emitter.position = point
// 3.开启三维效果
emitter.preservesDepth = true
// 4.创建粒子,并且设置粒子相关属性
// 4.1 创建粒子 cell
let cell = CAEmitterCell()
// 4.2 设置粒子速度
cell.velocity = 150
cell.velocity = 100
// 4.3 设置粒子的大小
cell.scale = 0.7
cell.scaleRange = 0.3
// 4.4 设置粒子方向
cell.emissionLongitude = CGFloat(-Double.pi / 2)
cell.emissionRange = CGFloat(Double.pi / 4)
// 4.5 设置粒子存活时间
cell.lifetime = 6
cell.lifetimeRange = 1.5
// 4.6 设置粒子旋转
cell.spin = CGFloat(Double.pi / 2)
cell.spinRange = CGFloat(Double.pi / 4)
// 4.7 设置粒子每秒弹出的个数
cell.birthRate = 20
// 4.8 设置粒子展示图片
cell.contents = UIImage(named: "good2_30x30_")?.cgImage
// 5.将粒子加入到发射器中
emitter.emitterCells = [cell]
// 6.将发射的 layer 添加到父 layer 中
view.layer.addSublayer(emitter)
}
func stop() {
/*
for layer in view.layer.sublayers! {
if layer.isKind(of: CAEmitterLayer.self) {
layer.removeFromSuperlayer()
}
}
*/
view.layer.sublayers?.filter({ $0.isKind(of: CAEmitterLayer.self)}).first?.removeFromSuperlayer()
}
坐标转换
// 将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;
// 将像素point从view中转换到当前视图中,返回在当前视图中的像素值
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;
// 将rect由rect所在视图转换到目标视图view中,返回在目标视图view中的rect
- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;
// 将rect从view中转换到当前视图中,返回在当前视图中的rect
- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;
//例把UITableViewCell中的subview(btn)的frame转换到 controllerA中
// controllerA 中有一个UITableView, UITableView里有多行UITableVieCell,cell上放有一个button
// 在controllerA中实现:
CGRect rc = [cell convertRect:cell.btn.frame toView:self.view];
或
CGRect rc = [self.view convertRect:cell.btn.frame fromView:cell];
// 此rc为btn在controllerA中的rect
//或当已知btn时:
CGRect rc = [btn.superview convertRect:btn.frame toView:self.view];
或
CGRect rc = [self.view convertRect:btn.frame fromView:btn.superview];
通过按钮的点击实现一个功能的开启和关闭
button.isSelected = !sender.isSelected
button.isSelected ? emitterAnimationStart(point!) : stopEmitterAnimation()
CLOPageView 拓展
主要思路:对中间的
collectionView
自定义layout
网友评论