Reputation: 644
I want both the welcome view controller and ViewController2 to have a UIVisualEffectView for their background. When I push ViewController2, the animation is very glitchy and doesn't fully animate to the left during the push animation. Pop animation works fine. Is there anyway to fix this weird animation?
Minimum Reproducible Example is below
class ViewController: UIViewController {
override func loadView() {
view = MKMapView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
let welcomeVC = WelcomeViewController()
let navigationController = UINavigationController(rootViewController: welcomeVC)
navigationController.isModalInPresentation = true
if let sheet = navigationController.sheetPresentationController {
sheet.detents = [.custom { context in
return 200
}, .medium(), .large()]
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 15
sheet.largestUndimmedDetentIdentifier = .large
}
present(navigationController, animated: true)
}
}
class WelcomeViewController: UIViewController {
override func viewDidLoad() {
let blurEffect = UIBlurEffect(style: .systemThinMaterial)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
view.addSubview(blurEffectView)
view.sendSubviewToBack(blurEffectView)
let button = UIButton()
button.setTitle("Go to View Controller 2", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func buttonTapped() {
let vc2 = ViewController2()
navigationController?.pushViewController(vc2, animated: true)
}
}
class ViewController2: UIViewController {
override func viewDidLoad() {
let blurEffect = UIBlurEffect(style: .systemThinMaterial)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
view.addSubview(blurEffectView)
view.sendSubviewToBack(blurEffectView)
let button = UIButton()
button.setTitle("Go back to Welcome VC", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func buttonTapped() {
navigationController?.popViewController(animated: true)
}
}
Upvotes: 0
Views: 78
Reputation: 77672
When using a UINavigationController
, the default transition is not a direct "slide side-by-side." The controller views overlap and fade.
You have a couple options...
UIPageViewController
UIScrollView
as a "container" and load your view controllers as childrenWhich approach to use depends on a number of factors -- such as how many controllers you want to show, do you want interactive drag-to-slide, etc.
Probably the most straightforward approach would be the "container" approach.
Here's a complete, minimal example, based on your code:
class ExampleViewController: UIViewController {
override func loadView() {
view = MKMapView()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
let containerVC = ExampleContainerVC()
containerVC.isModalInPresentation = true
if let sheet = containerVC.sheetPresentationController {
sheet.detents = [.custom { context in
return 200
}, .medium(), .large()]
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 15
sheet.largestUndimmedDetentIdentifier = .large
}
present(containerVC, animated: true)
}
}
protocol ContainerNavDelegate: AnyObject {
func showV2()
func showWelcome()
}
class ExampleContainerVC: UIViewController, ContainerNavDelegate {
let scrollView = UIScrollView()
let stackView = UIStackView()
override func viewDidLoad() {
let blurEffect = UIBlurEffect(style: .systemThinMaterial)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
view.addSubview(blurEffectView)
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.isScrollEnabled = false
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
let g = view.safeAreaLayoutGuide
let cg = scrollView.contentLayoutGuide
let fg = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
stackView.topAnchor.constraint(equalTo: cg.topAnchor),
stackView.leadingAnchor.constraint(equalTo: cg.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: cg.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: cg.bottomAnchor),
stackView.heightAnchor.constraint(equalTo: fg.heightAnchor),
])
let vc1 = WelcomeVC()
addChild(vc1)
stackView.addArrangedSubview(vc1.view)
vc1.view.widthAnchor.constraint(equalTo: fg.widthAnchor).isActive = true
vc1.didMove(toParent: self)
let vc2 = SecondVC()
addChild(vc2)
stackView.addArrangedSubview(vc2.view)
vc2.view.widthAnchor.constraint(equalTo: fg.widthAnchor).isActive = true
vc2.didMove(toParent: self)
vc1.navDelegate = self
vc2.navDelegate = self
}
func showV2() {
guard stackView.arrangedSubviews.count == 2 else { return }
let v = stackView.arrangedSubviews[1]
scrollView.scrollRectToVisible(v.frame, animated: true)
}
func showWelcome() {
guard stackView.arrangedSubviews.count > 0 else { return }
let v = stackView.arrangedSubviews[0]
scrollView.scrollRectToVisible(v.frame, animated: true)
}
}
class WelcomeVC: UIViewController {
var navDelegate: ContainerNavDelegate?
override func viewDidLoad() {
view.backgroundColor = .clear
let button = UIButton()
button.setTitle("Go to View Controller 2", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func buttonTapped() {
navDelegate?.showV2()
}
}
class SecondVC: UIViewController {
var navDelegate: ContainerNavDelegate?
override func viewDidLoad() {
view.backgroundColor = .clear
let button = UIButton()
button.setTitle("Go back to Welcome VC", for: .normal)
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 44)
])
}
@objc func buttonTapped() {
navDelegate?.showWelcome()
}
}
Upvotes: 0