提问人:Kenton 提问时间:11/14/2023 最后编辑:Kenton 更新时间:11/14/2023 访问量:36
使用 UIViewPropertyAnimator 在 TableView 中中断滚动
Broken scrolling in TableView with UIViewPropertyAnimator
问:
请帮我解决这个错误。我有一个 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)
}
}
}
答:
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
评论