使用 UIViewPropertyAnimator 在 TableView 中中断滚动

Broken scrolling in TableView with UIViewPropertyAnimator

提问人:Kenton 提问时间:11/14/2023 最后编辑:Kenton 更新时间:11/14/2023 访问量:36

问:

请帮我解决这个错误。我有一个 ViewController (MainVC),这个 VC 有一个子 View Controller (MainChildVC)。我可以在我的 MainVC 中看到子 VC 的顶部。当我拖动 childVC 时 - 没关系,可以正确打开,但是当我尝试在 childVC 中滚动我的 tableView 时 - childVC 返回到其原始位置并且不会再次打开。 请帮助解决它。

这是我的 MainChildVC 代码:

    enum ChildVCState {
        case expanded
        case collapsed
    }
    
    class MainVC: UIViewController {

var mainChild: MainChildVC!
lazy var mainChildHeight: CGFloat = view.frame.size.height - 50
    let mainChildHanldeAreaHeight = 80

var runningAnimations = [UIViewPropertyAnimator]()
    var animationProgressWhenInterrupted: CGFloat = 0
    
    override func loadView() {
        super.loadView()
        view = mainViews
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        self.definesPresentationContext = true
        setupChild()
    }

    private func setupChild() {
        mainChild = MainChildVC()
        addChild(mainChild)
        view.insertSubview(mainChild.view, at: 2)
        mainChild.didMove(toParent: self)
        mainChild.view.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            mainChild.view.topAnchor.constraint(equalTo: mainViews.waveAnimationView.bottomAnchor, constant: -10),
            mainChild.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
            mainChild.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
            mainChild.view.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1.0)
        ])
        mainChild.view.clipsToBounds = true
        
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MainVC.handleMainChildTap(recognizer:)))
        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(MainVC.handleMainChildPan(recognizer:)))
        
//        mainChild.mainChildViews.handleArea.addGestureRecognizer(panGestureRecognizer)
//        mainChild.mainChildViews.handleArea.addGestureRecognizer(tapGestureRecognizer)
        mainChild.view.addGestureRecognizer(tapGestureRecognizer)
        mainChild.view.addGestureRecognizer(panGestureRecognizer)
    }
    @objc private func handleMainChildTap(recognizer: UITapGestureRecognizer) {
        switch recognizer.state {
        case .ended:
            animateTransitionIfNeeded(state: nexnState, duration: 0.9)
        default:
            break
        }
    }
    
    @objc private func handleMainChildPan(recognizer: UIPanGestureRecognizer) {
        switch recognizer.state {
        case .began:
            //start animation
            startInteractiveTransition(state: nexnState, duration: 0.9)
        case .changed:
            //update transition
            let translation = recognizer.translation(in: mainChild.mainChildViews.handleArea)
            var fractionComplete = translation.y / self.mainChildHeight
            fractionComplete = mainChildVisible ? fractionComplete : -fractionComplete
            updateInteractiveTransition(fractionCompleted: fractionComplete)
        case .ended:
            //continue transition
            continueInteractiveTransition()
        default:
            break
        }
    }
    
    private func animateTransitionIfNeeded(state: ChildVCState, duration: TimeInterval) {
        if runningAnimations.isEmpty {
            let frameAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1) {
                switch state {
                case .expanded:
                    self.mainChild.view.frame.origin.y = self.view.frame.height - self.view.frame.height + 40 //self.mainChildHeight
                case .collapsed:
                    self.mainChild.view.frame.origin.y = self.view.frame.height - self.view.frame.height * 0.4
                }
            }
            frameAnimator.addCompletion { _ in
                self.mainChildVisible = !self.mainChildVisible
                self.runningAnimations.removeAll()
            }
            frameAnimator.startAnimation()
            runningAnimations.append(frameAnimator)
            
            let cornerRadiusAnimator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
                switch state {
                case .expanded:
                    self.mainChild.view.layer.cornerRadius = 12
                    //self.view.alpha = 0
                    self.mainChild.view.alpha = 1
                    self.mainChild.view.isUserInteractionEnabled = true
                    self.mainChild.mainChildViews.tableView.isUserInteractionEnabled = true
                    self.mainChild.mainChildViews.handleArea.backgroundColor = .black
                case .collapsed:
                    self.view.alpha = 1
                    self.mainChild.view.layer.cornerRadius = 0
                    self.mainChild.mainChildViews.tableView.isUserInteractionEnabled = false
                    self.mainChild.mainChildViews.handleArea.backgroundColor = .green
                }
            }
            cornerRadiusAnimator.startAnimation()
            runningAnimations.append(cornerRadiusAnimator)
        }
    }
    private func startInteractiveTransition(state: ChildVCState, duration: TimeInterval) {
        if runningAnimations.isEmpty {
            // run animation
            animateTransitionIfNeeded(state: state, duration: duration)
        }
        for animator in runningAnimations {
            animator.pauseAnimation()
            animationProgressWhenInterrupted = animator.fractionComplete
        }
    }
    
    private func updateInteractiveTransition(fractionCompleted: CGFloat) {
        for animator in runningAnimations {
            animator.fractionComplete = fractionCompleted + animationProgressWhenInterrupted
        }
    }
    
    private func continueInteractiveTransition() {
        for animator in runningAnimations {
            animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
        }
    }

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

这是我的 ChildVC 代码:

import UIKit
import youtube_ios_player_helper

final class MainChildVC: UIViewController {
    
    let mainChildViews = MainChildViews()
    
    override func loadView() {
        super.loadView()
        view = mainChildViews
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.alpha = 1
        view.backgroundColor = .black
        _ = Timer.scheduledTimer(timeInterval: 20, target: self, selector: #selector(updateCellData), userInfo: nil, repeats: true)
        
        mainChildViews.tableView.delegate = self
        mainChildViews.tableView.dataSource = self
                
        tabBarController?.tabBar.isTranslucent = true
        tabBarController?.tabBar.backgroundImage = UIImage()
        tabBarController?.tabBar.shadowImage = UIImage()
        tabBarController?.tabBar.backgroundColor = UIColor.clear
        tabBarController?.tabBar.barTintColor = UIColor.clear
        
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        view.isUserInteractionEnabled = true
        //view.alpha = 1
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        view.alpha = 1.0
        
    }
                                                                
    
    @objc private func updateCellData() {
        if let previousCell = mainChildViews.tableView.cellForRow(at: IndexPath(row: 0, section: 4)) as? ClipsTableViewCell {
            previousCell.playerView.stopVideo()
        }
        videoIDs[0] = videoIDs.randomElement()!
        mainChildViews.tableView.reloadSections(IndexSet(integer: 4), with: .left)
    }
    
  }
}
Swift Xcode TableView 拖动 UIViewiePropertyAnimator

评论


答:

0赞 Kenton 11/14/2023 #1

好的,我刚刚解决了这个问题。问题出在我子视图的顶部约束上:

mainChild.view.topAnchor.constraint(equalTo: mainViews.waveAnimationView.bottomAnchor, constant: -10)

我删除了它,并在主视图控制器的变量中添加了约束:

private var childViewHeight = NSLayoutConstraint()
private var childViewBottom = NSLayoutConstraint()

在setupChild方法中,我设置了以下约束:

childViewHeight = mainChild.view.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.4)
childViewHeight.isActive = true

然后在方法中,我添加了上面的开关语句:animateTransitionIfNeeded

        self.childViewHeight.isActive = false
        self.childViewHeight = self.mainChild.view.heightAnchor.constraint(equalTo: self.view.heightAnchor)
        self.childViewHeight.isActive = true
        self.view.layoutIfNeeded()

只需更改以前的值并设置新值即可。所以它帮助了我。childViewHeight