Plese, help me with resolving this bug. I have first ViewController (MainVC) and this VC has a child View Controller (MainChildVC). I can see top of child VC in my MainVC. When I dragging childVC - it's ok, opens correctly, but when I try to scroll my tableView in childVC - childVC returns to its original position and doesn't open again. Please, help to resolve it.
Here is my MainChildVC code:
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
}
}
Here is my ChildVC code:
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)
}
}
}
Ok, I've just fixed this issue. The problem was with top constraint of my child view:
mainChild.view.topAnchor.constraint(equalTo: mainViews.waveAnimationView.bottomAnchor, constant: -10)
I deleted it and added constraints in variables in my Main View Controller:
private var childViewHeight = NSLayoutConstraint()
private var childViewBottom = NSLayoutConstraint()
In setupChild method I set these constraints:
childViewHeight = mainChild.view.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.4)
childViewHeight.isActive = true
Then in animateTransitionIfNeeded
method I added this above switch statements:
self.childViewHeight.isActive = false
self.childViewHeight = self.mainChild.view.heightAnchor.constraint(equalTo: self.view.heightAnchor)
self.childViewHeight.isActive = true
self.view.layoutIfNeeded()
Just changing previous value in childViewHeight
and set new value. So it helped me.