美文网首页
iOS 笔记 | 仿苹果地图的可拖动 view

iOS 笔记 | 仿苹果地图的可拖动 view

作者: 无夜之星辰 | 来源:发表于2020-04-21 00:27 被阅读0次
农药约吗?稳定上分的那种😆

前言

最近项目开发正好遇到了类似苹果地图中那个可以拖动的 view:

苹果地图

感觉有点意思,于是实现了一波。

需求

需满足三点:

  1. view 跟随手势而动。
  2. 支持快速向上刨和向下刨。
  3. 拖动过程中停止拖动,然后手指离屏,view 向上弹还是往回缩由 view 当前的高度决定。

实现

首先是 view 跟随手势而动,这个很简单,对应方法是: - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;,在里面做相关处理即可:

// 拖动过程中
private func handleMoved(touch: UITouch) {

    // 拿到当前 point
    let currentPoint: CGPoint = (touch.location(in: panView))
    // 拿到上一个 point
    let prePoint: CGPoint = (touch.previousLocation(in: panView))
    
    // 这是拖动的距离
    let offsetY = currentPoint.y - prePoint.y
    // 得到当前的高度
    let height = self.panView.frame.size.height - offsetY
    
    // 拖动过程修改高度,不能比最大值大,不能比最小值小(根据你们需求而定)
    if height <= panViewMaxHeight, height >= panViewMinHeight {
        self.panView.snp.remakeConstraints { (make) in
            make.height.equalTo(height)
            make.left.right.bottom.equalToSuperview()
        }
    }
    
}

然后第二、三点需求都是在拖动结束时处理,对应方法是:- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;,这里需要知道拖动结束时 view 到底是向上、向下还是处于静止状态。

拖动方向我们只需要根据两个点就可以知晓:拖动结束瞬间的那个点,及拖动结束瞬间前一瞬间的那个点:

核心代码:

let currentPoint: CGPoint = (touch.location(in: panView))
let prePoint: CGPoint = (touch.previousLocation(in: panView))

// 获取拖动方向
// 0.0 表示静止(可以用 绝对值<1 表示,非常非常轻微的拖动,看作是静止)
// 大于0表示向下
// 小于0表示向上
let offsetY = currentPoint.y - prePoint.y

这里需要注意的是理论上offsetY == 0是表示拖动结束时是绝对静止的,但是,很多时候我们认为是静止的实际却可能有一丢丢的偏差,也就是说offsetY并不等于0。对于这种情况我的处理方式是判断offsetY的绝对值,如果它的绝对值小于1,那么可以认为拖动结束时 view 是静止的。

提供一个完整 demo 代码

import UIKit

class GaodeMapPanDemoViewController: UIViewController {
    
    // 最大高度
    let panViewMaxHeight: CGFloat = 500
    // 最小高度
    let panViewMinHeight: CGFloat = 200
    
    private lazy var panView: PanView = {
        let view = PanView()
        view.touchMovedClosure = { [weak self] (touch) in
            self?.handleMoved(touch: touch)
        }
        view.touchEndedClosure = { [weak self] (touch) in
            self?.handleMovedEnded(touch: touch)
        }
        return view
    }()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        view.backgroundColor = .white
        
        view.addSubview(panView)
        panView.snp.makeConstraints { (make) in
            make.left.right.bottom.equalToSuperview()
            make.height.equalTo(panViewMinHeight)
        }
        
    }
    
    
    // 拖动过程中
    private func handleMoved(touch: UITouch) {
        
        // 拿到当前 point
        let currentPoint: CGPoint = (touch.location(in: panView))
        // 拿到上一个 point
        let prePoint: CGPoint = (touch.previousLocation(in: panView))
        
        // 这是拖动的距离
        let offsetY = currentPoint.y - prePoint.y
        // 得到当前的高度
        let height = self.panView.frame.size.height - offsetY
        
        // 拖动过程修改高度,不能比最大值大,不能比最小值小(根据你们需求而定)
        if height <= panViewMaxHeight, height >= panViewMinHeight {
            self.panView.snp.remakeConstraints { (make) in
                make.height.equalTo(height)
                make.left.right.bottom.equalToSuperview()
            }
        }
        
    }
    
    
    // 拖动结束
    private func handleMovedEnded(touch: UITouch) {
        
        let currentPoint: CGPoint = (touch.location(in: panView))
        let prePoint: CGPoint = (touch.previousLocation(in: panView))
        
        let offsetY = currentPoint.y - prePoint.y
        
        // 获取拖动方向
        // 0.0 表示静止(可以用 绝对值<1 表示,非常非常轻微的拖动,看作是静止)
        // 大于0表示向下
        // 小于0表示向上
        if abs(offsetY) <= 1 {
            if self.panView.height() < (panViewMaxHeight+panViewMinHeight)/2 {
                UIView.animate(withDuration: 0.1) {
                    self.panView.snp.remakeConstraints { (make) in
                        make.left.right.bottom.equalToSuperview()
                        make.height.equalTo(self.panViewMinHeight)
                    }
                    self.panView.superview?.layoutIfNeeded()
                }
            } else {
                UIView.animate(withDuration: 0.1) {
                    self.panView.snp.remakeConstraints { (make) in
                        make.left.right.bottom.equalToSuperview()
                        make.height.equalTo(self.panViewMaxHeight)
                    }
                    self.panView.superview?.layoutIfNeeded()
                }
            }
        } else if offsetY > 0 {
            UIView.animate(withDuration: 0.1) {
                self.panView.snp.remakeConstraints { (make) in
                    make.left.right.bottom.equalToSuperview()
                    make.height.equalTo(self.panViewMinHeight)
                }
                self.panView.superview?.layoutIfNeeded()
            }
        } else {
            UIView.animate(withDuration: 0.1) {
                self.panView.snp.remakeConstraints { (make) in
                    make.left.right.bottom.equalToSuperview()
                    make.height.equalTo(self.panViewMaxHeight)
                }
                self.panView.superview?.layoutIfNeeded()
            }
        }
        
    }
    
    
}

// 可拖动的 view
fileprivate class PanView: UIView {
    
    var touchMovedClosure: ((_ touch: UITouch)->())?
    var touchEndedClosure: ((_ touch: UITouch)->())?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = UIColor.colorWithHexString("#EBEDF0")
        
        self.layer.cornerRadius = 20
        
        self.layer.shadowColor = UIColor.colorWithHexString("#000000").withAlphaComponent(0.3).cgColor
        self.layer.shadowOffset = .init(width: 0, height: 2)
        self.layer.shadowOpacity = 1
        self.layer.shadowRadius = 12
        
        let lineView = UIView()
        addSubview(lineView)
        lineView.backgroundColor = UIColor.colorWithHexString("#C2C7CC")
        lineView.layer.cornerRadius = 2
        lineView.snp.makeConstraints { (make) in
            make.width.equalTo(36)
            make.height.equalTo(4)
            make.centerX.equalToSuperview()
            make.top.equalTo(12)
        }
        
        let label = UILabel()
        addSubview(label)
        label.text = "盘它"
        label.textColor = .red
        label.font = .boldSystemFont(ofSize: 22)
        label.snp.makeConstraints { (make) in
            make.top.equalTo(35)
            make.centerX.equalToSuperview()
        }
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        if let touch = touches.first {
            touchMovedClosure?(touch)
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if let touch = touches.first {
            touchEndedClosure?(touch)
        }
    }
    
}

最后

请认真看文章开头那张图的说明,这才是最重要的。

相关文章

网友评论

      本文标题:iOS 笔记 | 仿苹果地图的可拖动 view

      本文链接:https://www.haomeiwen.com/subject/ynilihtx.html