I hope to create a specific custom transition animation for a button
that grows in size
to fill center of the screen overlaying the previous ViewController.
luckily I've even found how I want it to look like! (Reference))
I'm new to coding so I know this is something I shouldn't get into,
but I wanted to give it a go
and unlike in the reference, I planned to put just one textField & a button inside -
but it seems like I've been digging the wrong hole.
The overall animation doesn't look great either with the background visible (even with .clear)
to add on, I played around with much of the numbers...
here's a gif of what I made
(Test Animation)
Is there any way to salvage pieces of my code to recreate like the one in the reference?
and if it's not view (from my today's experience...) what would be a way to create it?
here's what I've done so far
---- Presenter ----
class Presenter: NSObject, UIViewControllerTransitioningDelegate {
private let transition = Transition()
func present(_ viewController: UIViewController, from parent: UIViewController) {
viewController.modalPresentationStyle = .overFullScreen
viewController.transitioningDelegate = self
parent.present(viewController, animated: true)
}
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.direction = .present
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.direction = .dismiss
return transition
}
}
---- Transition ----
class Transition: NSObject, UIViewControllerAnimatedTransitioning {
enum Direction {
case present
case dismiss
}
var direction: Direction = .present
private var presentedConstraints: [NSLayoutConstraint] = []
private var dismissedConstraints: [NSLayoutConstraint] = []
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 0.7 }
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch direction {
case .present:
present(using: transitionContext)
case .dismiss:
dismiss(using: transitionContext)
}
}
private func present(using context: UIViewControllerContextTransitioning) {
guard let presentedView = context.view(forKey: .to) else {
context.completeTransition(false)
return
}
context.containerView.addSubview(presentedView)
presentedView.translatesAutoresizingMaskIntoConstraints = false
presentedConstraints = [
presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor, constant: 10),
presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor, constant: -10),
presentedView.topAnchor.constraint(equalTo: context.containerView.topAnchor),
presentedView.bottomAnchor.constraint(equalTo: context.containerView.centerYAnchor, constant: 170)
]
dismissedConstraints = [
presentedView.leftAnchor.constraint(equalTo: context.containerView.leftAnchor, constant: 80),
presentedView.rightAnchor.constraint(equalTo: context.containerView.rightAnchor, constant: -80),
presentedView.topAnchor.constraint(equalTo: context.containerView.topAnchor, constant: 400),
presentedView.bottomAnchor.constraint(equalTo: context.containerView.bottomAnchor, constant: -30)
]
NSLayoutConstraint.activate(dismissedConstraints)
context.containerView.setNeedsLayout()
context.containerView.layoutIfNeeded()
NSLayoutConstraint.deactivate(dismissedConstraints)
NSLayoutConstraint.activate(presentedConstraints)
UIView.animate(
withDuration: transitionDuration(using: context),
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: .curveEaseInOut,
animations: {
context.containerView.setNeedsLayout()
context.containerView.layoutIfNeeded()
},
completion: { _ in
context.completeTransition(true)
})
}
private func dismiss(using context: UIViewControllerContextTransitioning) {
NSLayoutConstraint.deactivate(presentedConstraints)
NSLayoutConstraint.activate(dismissedConstraints)
UIView.animate(
withDuration: transitionDuration(using: context),
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: .curveEaseInOut,
animations: {
context.containerView.setNeedsLayout()
context.containerView.layoutIfNeeded()
},
completion: { _ in
context.completeTransition(true)
})
}
}
and finally just the 2nd VC that has the view!
---- 2nd VC ----
protocol MessageViewControllerDelegate: AnyObject {
func messageViewController(_ vc: MessageViewController, didSaveTodo: Todo)
}
class MessageViewController: UIViewController {
weak var delegate: MessageViewControllerDelegate?
var todo: Todo?
let messageView = UIView()
let sendButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.addSubview(messageView)
view.addSubview(sendButton)
sendButton.translatesAutoresizingMaskIntoConstraints = false
sendButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
sendButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 50).isActive = true
sendButton.backgroundColor = .blue
sendButton.addTarget(self, action: #selector(save), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
messageView.backgroundColor = .yellow
messageView.layer.cornerRadius = 180/2
messageView.clipsToBounds = true
messageView.translatesAutoresizingMaskIntoConstraints = false
messageView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
messageView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
messageView.topAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
messageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
@objc func save(_ sender: UIButton) {
print("Save was tapped.")
dismiss(animated: true)
}
}