如何创建橡皮筋效果?

时间:2019-04-04 03:58:59

标签: ios swift

什么是如何实现橡皮筋效果的完整示例? 我该如何实现?

I have tried the following: However, I have been unsuccessful in finding how to implement this.

当前,我创建了一个卡片视图,该视图可以拉至特定点,但是目前,当您达到最大值时,会突然停止,我想更改为橡皮筋效果。

这是我一直在尝试添加的代码:

enum SheetLevel{
    case top, bottom, middle
}

protocol BottomSheetDelegate {
    func updateBottomSheet(frame: CGRect)
}

class BottomSheetViewController: UIViewController{

    @IBOutlet var panView: UIView!
    @IBOutlet weak var tableView: UICollectionView!
    //    @IBOutlet weak var collectionView: UICollectionView! //header view

    var lastY: CGFloat = 0
    var pan: UIPanGestureRecognizer!

    var bottomSheetDelegate: BottomSheetDelegate?
    var parentView: UIView!

    var initalFrame: CGRect!
    var topY: CGFloat = 80 //change this in viewWillAppear for top position
    var middleY: CGFloat = 400 //change this in viewWillAppear to decide if animate to top or bottom
    var bottomY: CGFloat = 600 //no need to change this
    let bottomOffset: CGFloat = 64 //sheet height on bottom position
    var lastLevel: SheetLevel = .middle //choose inital position of the sheet

    var disableTableScroll = false

    //hack panOffset To prevent jump when goes from top to down
    var panOffset: CGFloat = 0
    var applyPanOffset = false

    //tableview variables
    var listItems: [Any] = []
    var headerItems: [Any] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        pan.delegate = self
        self.panView.addGestureRecognizer(pan)

        self.tableView.panGestureRecognizer.addTarget(self, action: #selector(handlePan(_:)))

        //Bug fix #5. see https://github.com/OfTheWolf/UBottomSheet/issues/5
        //Tableview didselect works on second try sometimes so i use here a tap gesture recognizer instead of didselect method and find the table row tapped in the handleTap(_:) method
        let tap = UITapGestureRecognizer.init(target: self, action: #selector(handleTap(_:)))
        tap.delegate = self
        tableView.addGestureRecognizer(tap)
        //Bug fix #5 end
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.initalFrame = UIScreen.main.bounds
        self.topY = round(initalFrame.height * 0.5)
        self.middleY = initalFrame.height * 0.6
        self.bottomY = initalFrame.height - bottomOffset
        self.lastY = self.middleY

        bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: self.middleY))
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView == tableView else {return}

        if (self.parentView.frame.minY > topY){
            self.tableView.contentOffset.y = 0
        }
    }


    //this stops unintended tableview scrolling while animating to top
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        guard scrollView == tableView else {return}

        if disableTableScroll{
            targetContentOffset.pointee = scrollView.contentOffset
            disableTableScroll = false
        }
    }

    //Bug fix #5. see https://github.com/OfTheWolf/UBottomSheet/issues/5
    @objc func handleTap(_ recognizer: UITapGestureRecognizer) {
        let p = recognizer.location(in: self.tableView)
        //Commented below to prevenet error.. *** apr 2 guillermo
        //        let index = tableView.indexPathForRow(at: p)
        //        //WARNING: calling selectRow doesn't trigger tableView didselect delegate. So handle selected row here.
        //        //You can remove this line if you dont want to force select the cell
        //        tableView.selectRow(at: index, animated: false, scrollPosition: .none)
    }//Bug fix #5 end


    @objc func handlePan(_ recognizer: UIPanGestureRecognizer) {
//        var x = topY
//        var c = 0.55
//        var d = view.frame.height
//        var formula = (1.0 - (1.0 / ((x * c / d) + 1.0))) * d

        let dy = recognizer.translation(in: self.parentView).y
        print(recognizer.translation(in: self.parentView).y, " This si dy")
        switch recognizer.state {
        case .began:
            applyPanOffset = (self.tableView.contentOffset.y > 0)
        case .changed:
            print(".changed here")
            if self.tableView.contentOffset.y > 0{
                panOffset = dy
                return
            }

            if self.tableView.contentOffset.y <= 0 {
                if !applyPanOffset{panOffset = 0}
                let maxY = max(topY, lastY + dy - panOffset)
                let y = min(bottomY, maxY)
                print(y, ".inside if let thindfahfvdsgjafjsda8", maxY)
                //                self.panView.frame = self.initalFrame.offsetBy(dx: 0, dy: y)
                bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: y))
            }

            if self.parentView.frame.minY > topY{
                print(self.tableView.contentOffset.y, " Thsi is taht y vakue thing")
                self.tableView.contentOffset.y = 0
            }
        case .failed, .ended, .cancelled:
            panOffset = 0
            print(".failed and enededh rhere")

            //bug fix #6. see https://github.com/OfTheWolf/UBottomSheet/issues/6
            if (self.tableView.contentOffset.y > 0){
                return
            }//bug fix #6 end

            self.panView.isUserInteractionEnabled = false

            self.disableTableScroll = self.lastLevel != .top

            self.lastY = self.parentView.frame.minY
            self.lastLevel = self.nextLevel(recognizer: recognizer)

            UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.9, options: .curveEaseOut, animations: {
                print("Animation!!!!!")
                switch self.lastLevel {
                case .top:
                    print("in this thanaagaggagagagagagagalanfg")
                    //                    self.panView.frame = self.initalFrame.offsetBy(dx: 0, dy: self.topY)
//                    self.bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: self.topY))
//                    self.tableView.contentInset.bottom = 50
                    self.bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: self.middleY))

                case .middle:
                    //                    self.panView.frame = self.initalFrame.offsetBy(dx: 0, dy: self.middleY)
                    self.bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: self.middleY))
                case .bottom:
                    //                    self.panView.frame = self.initalFrame.offsetBy(dx: 0, dy: self.bottomY)
                    self.bottomSheetDelegate?.updateBottomSheet(frame: self.initalFrame.offsetBy(dx: 0, dy: self.bottomY))
                }

            }) { (_) in
                print("Someghtifgnshdfgbk")
                self.panView.isUserInteractionEnabled = true
                self.lastY = self.parentView.frame.minY
            }
        default:
            break
        }
    }

    func nextLevel(recognizer: UIPanGestureRecognizer) -> SheetLevel{
        let y = self.lastY
        let velY = recognizer.velocity(in: self.view).y
        if velY < -200{
            return y > middleY ? .middle : .top
        }else if velY > 200{
            return y < (middleY + 1) ? .middle : .bottom
        }else{
            if y > middleY {
                return (y - middleY) < (bottomY - y) ? .middle : .bottom
            }else{
                return (y - topY) < (middleY - y) ? .top : .middle
            }
        }
    }
}

extension BottomSheetViewController: UITableViewDelegate, UITableViewDataSource{
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SimpleTableCell", for: indexPath) as! SimpleTableCell
        let model = SimpleTableCellViewModel(image: nil, title: "Title \(indexPath.row)", subtitle: "Subtitle \(indexPath.row)")
        cell.configure(model: model)
        return cell
    }
}

extension BottomSheetViewController: UIGestureRecognizerDelegate{
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

}

1 个答案:

答案 0 :(得分:1)

您需要做的是通过拖动以指数方式减少用户添加的长度。为此有不同的公式,例如使用sqrt,log10等。

这就是我所做的:

Heading

这是代码:

import UIKit

class ViewController: UIViewController {

    lazy private var box : UIView = {
        let view = UIView(frame: CGRect(x: 0, y: UIScreen.main.bounds.height-300, width: UIScreen.main.bounds.width, height: 300))
        view.backgroundColor = .red
        return view
    }()

    private var panGesture : UIPanGestureRecognizer!
    private var boxOriginY : CGFloat = 300.0

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(box)

        panGesture = UIPanGestureRecognizer(target: self, action: #selector(pullUp(with:)))
        box.addGestureRecognizer(panGesture)
    }

    @objc private func pullUp(with pan: UIPanGestureRecognizer) {

        let yTranslation = pan.translation(in: self.view).y

        if pan.state == .changed {
            let distance = CGFloat(sqrt( Double(-yTranslation) ) * 10) // times 10 to make it smoother
            let newHeight : CGFloat = boxOriginY + distance

            box.frame = CGRect(x: 0,
                               y: UIScreen.main.bounds.height-(newHeight),
                               width: UIScreen.main.bounds.width,
                               height: newHeight)
        }

        if pan.state == .ended {
            if pan.state == UIPanGestureRecognizer.State.ended {
                UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
                    self.box.frame = CGRect(x: 0, y: UIScreen.main.bounds.height-300, width: UIScreen.main.bounds.width, height: 300)

                })
            }
        }
    }
}