I have a side navigation controller and present it via a UIButton. When I make this NC the root view controller directly by [self presentviewcontroller: NC animated: Y
To get tap events through the UITransitionView
, set the containerView
's userInteractionEnabled
to false
. This is if you're doing a custom transition animation by using UIViewControllerAnimatedTransitioning
.
Example, in your animateTransition(_:)
:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
containerView.isUserInteractionEnabled = false
...
}
In my situation I needed a halfSize view controller. I followed this answer which worked great until I realized I needed to still be able to interact with the presenting vc (the vc behind the halfSizeVC).
The key is that you have to set both of these frames with the same CGRect values:
halfSizeVC.frame = CGRect(x: 0, y: UIScreen.main.bounds.height / 2, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
containerView = CGRect(x: 0, y: UIScreen.main.bounds.height / 2, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
Here is the code to go from ViewController to HalfSizeController and make HalfSizeController 1/2 the screen size. Even with halfSizeVC
on screen you will still be able to interact with the top half of the vc behind it.
You also have to make a PassthroughView
class if you want to be able to touch something inside the halfSizeVC. I included it at the bottom.
The presenting vc is white with a purple button at the bottom. Tapping the purple button will bring up the red halfSizeVC.
vc/presentingVC:
import UIKit
class ViewController: UIViewController {
lazy var purpleButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Tap to Present HalfSizeVC", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = UIColor.systemPurple
button.addTarget(self, action: #selector(purpleButtonPressed), for: .touchUpInside)
button.layer.cornerRadius = 7
button.layer.masksToBounds = true
return button
}()
var halfSizeVC: HalfSizeController?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// tap gesture on vc will dismiss HalfSizeVC
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissHalfSizeVC))
view.addGestureRecognizer(tapGesture)
}
// tapping the purple button presents HalfSizeVC
@objc func purpleButtonPressed() {
halfSizeVC = HalfSizeController()
// *** IMPORTANT ***
halfSizeVC!.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)
halfSizeVC!.modalPresentationStyle = .custom
present(halfSizeVC!, animated: true, completion: nil)
}
// dismiss HalfSizeVC by tapping anywhere on the white background
@objc func dismissHalfSizeVC() {
halfSizeVC?.dismissVC()
}
}
halfSizeVC/presentedVC
import UIKit
class HalfSizeController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .custom
transitioningDelegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var topHalfDummyView: PassthroughView = {
let view = PassthroughView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .clear
view.isUserInteractionEnabled = true
return view
}()
var isPresenting = false
let halfScreenHeight = UIScreen.main.bounds.height / 2
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
setAnchors()
}
private func setAnchors() {
view.addSubview(topHalfDummyView)
topHalfDummyView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
topHalfDummyView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
topHalfDummyView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
topHalfDummyView.heightAnchor.constraint(equalToConstant: halfScreenHeight).isActive = true
}
public func dismissVC() {
dismiss(animated: true, completion: nil)
}
}
extension HalfSizeController: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 1
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
// *** IMPORTANT ***
containerView.frame = CGRect(x: 0, y: halfScreenHeight, width: UIScreen.main.bounds.width, height: halfScreenHeight)
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
guard let toVC = toViewController else { return }
isPresenting = !isPresenting
if isPresenting == true {
containerView.addSubview(toVC.view)
topHalfDummyView.frame.origin.y += halfScreenHeight
UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {
self.topHalfDummyView.frame.origin.y -= self.halfScreenHeight
}, completion: { (finished) in
transitionContext.completeTransition(true)
})
} else {
UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {
}, completion: { (finished) in
self.topHalfDummyView.frame.origin.y += self.halfScreenHeight
transitionContext.completeTransition(true)
})
}
}
}
PassthroughView needed for the topHalfDummyView in HalfSizeVC
import UIKit
class PassthroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
}
Before purple button is pressed:
After purple button is pressed:
If you press the white background the red color will get dismissed
You can just c+p all 3 files and run your project
I was facing the same issue, and this solved issue for me,
navigationController.setNavigationBarHidden(true, animated: false)
This worked for me as I am having custom view as navigation bar in view controllers.
I had a similar issue where a UITransitionView kept blocking my views, preventing any user interaction.
In my case this was due to an uncompleted custom animated UIViewController transition.
I forgot to properly complete my transition with:
TransitionContext.completeTransition(transitionContext.transitionWasCancelled)
or
TransitionContext.completeTransition(!transitionContext.transitionWasCancelled)
In the
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {}
from the UIViewControllerAnimatedTransitioning
protocol
I had the same issue but in a little different scenario, I ended up doing something very similar to find the view but instead of removing the view which can be more problematic I disabled the user interaction so any touch events just go throw it and any other objects can handle to user's interaction. In my case this was only present after updating the app to iOS 10, the same code running in iOS 9 didn't fall into this.
Ive had this issue when I was setting accessibilityElements on a popover view controller. I fix it by removing assigning an array of elements.