在项目不忙之余,心血来潮突然想仿写一个ofo,本人会把项目中写的控件及技术点罗列出来,与同僚共同学习的同时,也是对自己学习的总结。
本章节仅实现ofo底部扫码视图弹出效果。
贝塞尔曲线画圆角
首先底部视图上的弧度是通过Quart2D实现,首先在View
引出drawRect
方法。
drawRect
方法提供UIView
的重绘机制,可以在此方法中进行绘图,因此类教程颇多具体就不细说了。
上代码:
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
// 此为移动到的位置
ctx?.move(to: CGPoint(x: SCREEN_W, y: 50))
// 添加贝塞尔曲线 control 参数表弧线坐标点
ctx?.addQuadCurve(to: CGPoint(x: 0, y: 50), control: CGPoint(x: SCREEN_W/2, y: -33))
// 从左下角至移动点画一条线
ctx?.addLine(to: CGPoint(x: 0, y: self.height))
// 从右下角至移动点画一条线
ctx?.addLine(to: CGPoint(x: SCREEN_W, y: self.height))
// 填充背景为白色
ctx?.setFillColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))
// 设置线条阴影
ctx?.setShadow(offset: CGSize(width: 0, height: 0), blur: 0.8, color: #colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1))
// 此处fill表示仅填充
ctx?.drawPath(using: .fill)
}
拖动手势
此处功能很简单,在底部UIView
上创建上划及下滑手势,因打开APP默认底部视图是展开所以此处设置topRecognizer.isEnabled
默认为false
// MARK: - 上划手势
lazy var topRecognizer: UISwipeGestureRecognizer = {
let topRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeFrom(recognizer:)))
topRecognizer.isEnabled = false
topRecognizer.direction = .up
return topRecognizer
}()
// MARK: - 下划手势
lazy var downRecognizer: UISwipeGestureRecognizer = {
let downRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeFrom(recognizer:)))
downRecognizer.direction = .down
return downRecognizer
}()
通过判断滑动手势来处理上划还是下划,并且与点击∧
or∨
方法公用
// MARK: - 手势滑动方法
@objc func handleSwipeFrom(recognizer: UISwipeGestureRecognizer) {
if recognizer.direction == .up {
changeArrow()
} else if recognizer.direction == .down {
changeArrow()
}
}
// MARK: - 视图打开or回收
private func changeArrow() {
if isHiddenBottomView == false {
arrowButton.setImage(#imageLiteral(resourceName: "arrowup"), for: .normal)
} else {
arrowButton.setImage(#imageLiteral(resourceName: "arrowdown"), for: .normal)
}
topRecognizer.isEnabled = !isHiddenBottomView
downRecognizer.isEnabled = isHiddenBottomView
isHiddenBottomView = !isHiddenBottomView
delegate?.didSelectedArrowButton(isHiddenBottomView: isHiddenBottomView)
}
didSelectedArrowButton
代理方法为返回到背景时控制View
约束的回调
// didSelectedArrowButton代理方法,控制点击`∧`or`∨` and 上划下滑
func didSelectedArrowButton(isHiddenBottomView: Bool) {
updateBottomViewWithExpand(isExpanded: isHiddenBottomView, animated: true)
}
// 此方法控制底部视图约束
private func updateBottomViewWithExpand(isExpanded:Bool = false, animated:Bool = false, duration:TimeInterval = 0.25) {
bottomView.snp.updateConstraints { (make) in
make.left.right.equalTo(self)
make.height.equalTo(280)
if isExpanded == true {
make.bottom.equalTo(snp.bottom).offset(210)
} else {
make.bottom.equalTo(snp.bottom)
}
}
if animated == true {
self.needsUpdateConstraints()
self.updateConstraintsIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.layoutIfNeeded()
}, completion: { (completion) in
self.bottomView.updateWithExpand(isExpanded: isExpanded, animated: true, duration: 0.2)
})
}
}
视图上划后扫码等按钮上移动画
updateBottomViewWithExpand
方法中,在动画结束后引出bottomView(底部视图)
的updateWithExpand
来更新bottomView(底部视图)
的约束,从而实现扫码等按钮跟随底部视图上移而上移的动画效果。
open func updateWithExpand(isExpanded:Bool = false, animated:Bool = false, duration:TimeInterval = 0.25) {
startButton.snp.updateConstraints { (make) in
make.centerX.equalTo(self)
make.width.equalTo(snp.width).multipliedBy(0.55)
make.height.equalTo(startButton.snp.width)
if isExpanded {
make.top.equalTo(arrowButton.snp.bottom).offset(30)
} else {
make.top.equalTo(arrowButton.snp.bottom).offset(15)
}
}
loginButton.snp.updateConstraints { (make) in
make.left.equalTo(snp.left).offset(20)
make.width.equalTo(30)
make.height.equalTo(loginButton.snp.width)
if isExpanded {
make.bottom.equalTo(snp.bottom).offset(30)
} else {
make.bottom.equalTo(snp.bottom).offset(-15)
}
}
newsButton.snp.updateConstraints { (make) in
make.right.equalTo(snp.right).offset(-20)
make.width.equalTo(30)
make.height.equalTo(newsButton.snp.width)
if isExpanded {
make.bottom.equalTo(snp.bottom).offset(15)
} else {
make.bottom.equalTo(snp.bottom).offset(-15)
}
}
if animated == true {
self.needsUpdateConstraints()
self.updateConstraintsIfNeeded()
UIView.animate(withDuration: duration, animations: {
self.layoutIfNeeded()
})
}
}
完整代码后续会进行上传~