How to dismiss UICollectionViewController from button in UICollectionViewCell - ios

I am making an onboarding screen, on the last screen I have a button that says "continue" and is supposed to dismiss the onboarding screen. The onboarding screen is a collection view controller with cells as each page. Please do not hesitate to ask for clarification I am don't know what else to add.
Thanks,
Edit
So I implemented user Francesco Deliro's answer, first problem was that I accidentally added the "delegate = self" into the viewDidLoad(). I fixed that but still the viewController does not dismiss.
My code is as follow in my viewController cell for item:
let loginCell = LoginCell()
loginCell.delegate = self
Here is the extension
extension TutorialViewController: LoginCellDelegate {
func didCompleteOnboarding() {
print("I should dimiss")
self.dismiss(animated: true, completion: nil)
}
Do I not need to call that function anywhere in the class just leave it outside the main class.
Edit 2
Here is how I connected my button action to the originial
#objc func continueTapped() {
...
continueButton.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
UIView.animate(withDuration: 1.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: .allowUserInteraction, animations: { [weak self] in
self?.continueButton.transform = .identity
let transition = CATransition()
transition.duration = 0.5
transition.type = CATransitionType.push
transition.subtype = CATransitionSubtype.fromRight
transition.timingFunction = CAMediaTimingFunction(name:CAMediaTimingFunctionName.easeInEaseOut)
self?.window!.layer.add(transition, forKey: kCATransition)
self?.delegate?.didCompleteOnboarding()
}, completion: { (success) in
token = true
defaults.set(token, forKey: "DidSee")
})
}

You can use delegation, for example:
protocol YourCellDelegate: class {
func didCompleteOnboarding()
}
Then in your cell:
class CustomCell: UICollectionViewCell {
weak var delegate: YourCellDelegate?
// in your button action
func dismissAction() {
delegate.didCompleteOnboarding()
}
}
Finally in your view controller set the cell delegate in the cellForItem function:
yourCell.delegate = self
And add:
extension YourViewController: YourCellDelegate {
func didCompleteOnboarding() {
// dismiss here
}
}

Related

How to open side menu from tabbar

I am trying to achieve some thing like following side menu open from tabbar item click.
I used the following class for Transition Animation ...
class SlideInTransition: NSObject, UIViewControllerAnimatedTransitioning {
var isPresenting = false
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width * 0.3
let finalHeight = toViewController.view.bounds.height
if isPresenting {
// Add dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
// Add menu view controller to container
containerView.addSubview(toViewController.view)
// Init frame off the screen
toViewController.view.frame = CGRect(x: -finalWidth, y: 0, width: finalWidth, height: finalHeight)
}
// Move on screen
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: finalWidth, y: 0)
}
// Move back off screen
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
// Animation of the transition
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
}
}
}
and use it in my code as follow
guard let menuViewController = storyboard?.instantiateViewController(withIdentifier: "MenuVC") as? MenuVC else { return }
menuViewController.modalPresentationStyle = .overCurrentContext
menuViewController.transitioningDelegate = self as? UIViewControllerTransitioningDelegate
menuViewController.tabBarItem.image = UIImage(named: "ico_menu")
menuViewController.tabBarItem.selectedImage = UIImage(named: "ico_menu")
viewControllers = [orderVC,serverdVC,canceledVC,menuViewController]
extension TabbarVC: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transiton.isPresenting = true
return transiton
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transiton.isPresenting = false
return transiton
}
}
but animation doe't work at all ... I want to open it like side menu over current context ..
How can i achieve some thing like that ...
TabBar is not made to handle animate transition for just a single child View controller. If you apply a custom transition, it will be applied in all of its tabs (child view controllers). Plus last time i checked, airbnb's app doesn't behave like that when opening the user profile. :)
What you can do, though, is have a separate menu button at the top of your navigation view controller or wherever and call the slide in from there:
func slideInView() {
let vcToShow = MenuViewController()
vcToShow.modalPresentationStyle = .overCurrentContext
vcToShow.transitioningDelegate = self as? UIViewControllerTransitioningDelegate
present(vcToShow, animated: true, completion: nil)
}
Or if you insist on having the menu a part of the tabs, then you can do this.
Hope this helps. :)

iOS: UILabel is not centered while expanding view with UIViewControllerTransitioningDelegate

I am trying to present modally a view when tapping a button, that would have at first the same frame than the button, and then expanding to end up full screen, all this using UIViewControllerTransitioningDelegate.
Here is my code:
Expandable Base
class ExpandableBase: UIViewController {
var senderFrame: CGRect = .zero
#IBOutlet weak var fullScreenPopupView: UIView?
#IBAction func dismiss() {
self.dismiss(animated: true, completion: nil)
}
}
Transitioning Delegate extension
extension ExpandableBase: UIViewControllerTransitioningDelegate {
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ExpandableBasePresenter()
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return ExpandableBaseDismisser()
}
}
private final class ExpandableBasePresenter: NSObject, UIViewControllerAnimatedTransitioning {
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.8
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let toViewController: ExpandableBase = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! ExpandableBase
let duration = self.transitionDuration(using: transitionContext)
let containerView = transitionContext.containerView
toViewController.view.frame = containerView.frame
containerView.addSubview(toViewController.view)
let finishFrame = toViewController.fullScreenPopupView?.frame
toViewController.fullScreenPopupView?.frame = toViewController.senderFrame
UIView.animate(withDuration: duration, delay: 0.3, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .layoutSubviews, animations: {
toViewController.fullScreenPopupView?.frame = finishFrame!
}) { result in
transitionContext.completeTransition(result)
}
}
}
private final class ExpandableBaseDismisser: NSObject, UIViewControllerAnimatedTransitioning {
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let fromViewController: ExpandableBase = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! ExpandableBase
let duration = self.transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, delay: 0.1, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .layoutSubviews, animations: {
fromViewController.fullScreenPopupView?.frame = fromViewController.senderFrame
}) { result in
transitionContext.completeTransition(result)
}
}
}
A simple view using this, presenting a label and a dismiss button:
final class ExpandableSimpleView: ExpandableBase {
init(from initialFrame: CGRect) {
super.init(nibName: "ExpandableSimpleView", bundle: .main)
self.senderFrame = initialFrame
self.transitioningDelegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("NOPE")
}
static func present(fromInitialFrame initialFrame: CGRect) {
let expandableSimpleView = ExpandableSimpleView(from: initialFrame)
expandableSimpleView.modalPresentationStyle = .overCurrentContext
AppDelegateTopMostViewController.present(expandableSimpleView, animated: true, completion: nil)
//AppDelegateTopMostViewController is a global reference to the top-most view controller of the app
}
}
Here is the corresponding XIB:
And how I present this from the parent view controller:
#IBAction func openSimpleView(_ sender: UIButton) {
ExpandableSimpleView.present(fromInitialFrame: sender.frame)
}
And here are some screenshots showing how this view expands:
Although the view expands fine, the label is not centered as it should be. I don't understand why.
Thank you for your help.
EDIT: following matt's answer, I have made the following changes in animateTransition's presenter.
toViewController.fullScreenPopupView!.transform =
toViewController.fullScreenPopupView!.transform.scaledBy(x: toViewController.senderFrame.width / toViewController.view.bounds.width, y: toViewController.senderFrame.height / toViewController.view.bounds.height)
UIView.animate(withDuration: duration, delay: 0, options: [.allowUserInteraction], animations: {
toViewController.fullScreenPopupView!.transform = .identity
}, completion: nil)
Now the animation is fine, but I'm facing another issue: the button in the view is not clickable any longer.
Your animation is wrong. Do not start with a small frame and animate an expansion of the frame. Start with a small transform (e.g. x and y scale values of 0.1) and expand the transform (to .identity).
There is nothing wrong with shrinking or growing the parent view.
You don't have to animate the actual views. You can create and remove temporary views. Hide the actual view and reveal it during takedown.
I suggest that you animate the UILabel separately. It doesn't have to shrink or grow. It merely has to remain stationary. Place a temporary UILabel over the original, hide the original, and perform the animation. Reverse the process during takedown.

How to present view controller from left to right in iOS?

When adding a new controller to the navigation stack:
self.navigationController!.pushViewController(PushedViewController(), animated: true)
it appears from the right:
How can I change the direction of animation to make it appear from the left?
Swift 5.1: Segue from different directions
Here is a simple extension for different segue directions. (Tested in Swift 5)
It looks like you want to use segueFromLeft() I added some other examples as well.
extension CATransition {
//New viewController will appear from bottom of screen.
func segueFromBottom() -> CATransition {
self.duration = 0.375 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromTop
return self
}
//New viewController will appear from top of screen.
func segueFromTop() -> CATransition {
self.duration = 0.375 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromBottom
return self
}
//New viewController will appear from left side of screen.
func segueFromLeft() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.moveIn
self.subtype = CATransitionSubtype.fromLeft
return self
}
//New viewController will pop from right side of screen.
func popFromRight() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.reveal
self.subtype = CATransitionSubtype.fromRight
return self
}
//New viewController will appear from left side of screen.
func popFromLeft() -> CATransition {
self.duration = 0.1 //set the duration to whatever you'd like.
self.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
self.type = CATransitionType.reveal
self.subtype = CATransitionSubtype.fromLeft
return self
}
}
And here is how you implement the above extension:
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(YourViewController(), animated: false)
}
let obj = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")as! ViewController
let transition:CATransition = CATransition()
transition.duration = 0.3
transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
transition.type = .push
transition.subtype = .fromLeft
self.navigationController?.view.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(obj, animated: true)
Whene you use popToViewController that Time
transition.subtype = kCATransitionFromRight
This may help you
let nextVc = self.storyboard?.instantiateViewController(withIdentifier: "nextVc")
let transition = CATransition()
transition.duration = 0.5
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromLeft
transition.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
view.window!.layer.add(transition, forKey: kCATransition)
self.navigationController?.pushViewController(nextVc!, animated: false)
Ok, here's a drop-in solution for you. Add file named LeftToRightTransitionProxy.swift with the next content
import UIKit
final class LeftToRightTransitionProxy: NSObject {
func setup(with controller: UINavigationController) {
controller.delegate = self
}
}
extension LeftToRightTransitionProxy: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return AnimationController(direction: .forward)
} else {
return AnimationController(direction: .backward)
}
}
}
private final class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
enum Direction {
case forward, backward
}
let direction: Direction
init(direction: Direction) {
self.direction = direction
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toView = transitionContext.view(forKey: .to),
let fromView = transitionContext.view(forKey: .from) else {
return
}
let container = transitionContext.containerView
container.addSubview(toView)
let initialX: CGFloat
switch direction {
case .forward: initialX = -fromView.bounds.width
case .backward: initialX = fromView.bounds.width
}
toView.frame = CGRect(origin: CGPoint(x: initialX, y: 0), size: toView.bounds.size)
let animation: () -> Void = {
toView.frame = CGRect(origin: .zero, size: toView.bounds.size)
}
let completion: (Bool) -> Void = { _ in
let success = !transitionContext.transitionWasCancelled
if !success {
toView.removeFromSuperview()
}
transitionContext.completeTransition(success)
}
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
animations: animation,
completion: completion
)
}
}
And here's how you can use it:
final class ViewController: UIViewController {
let animationProxy = LeftToRightTransitionProxy()
override func viewDidLoad() {
super.viewDidLoad()
animationProxy.setup(with: navigationController!)
}
}
This solution provides animation for both forward and backward (push and pop) directions.
This can be controlled in navigationController(_:animationControllerFor:from:to:) method of your LeftToRightTransitionProxy class (just return nil to remove animation).
If you need this behaviour for specific subclass of UIViewController put appropriate checks in navigationController(_:animationControllerFor:from:to:) method:
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push && toVC is DetailViewController {
return AnimationController(direction: .forward)
} else if operation == .pop && toVC is ViewController {
return AnimationController(direction: .backward)
}
return nil
}
I used Hero as a solution.
import Hero
Then in that place where you’re going to show new UIViewController turn the default animation:
Hero.shared.defaultAnimation = HeroDefaultAnimationType.cover(direction: .right)
Also specify that your UINavigationController is going to use the Hero library:
self.navigationController?.hero.isEnabled = true
After that you’ll get the expected result even if you’re using the standard pushViewController function:
self.navigationController?.pushViewController(vc, animated: true)
You'll need to write your own transition procedure to achieve your needs.
DOCS from Apple:
https://developer.apple.com/documentation/uikit/uiviewcontrollercontexttransitioning
Article:
https://medium.com/#ludvigeriksson/custom-interactive-uinavigationcontroller-transition-animations-in-swift-4-a4b5e0cefb1e
If you want to learn how to do custom transitions (i.e. presenting from right to left) then this is a pretty good tutorial for setting them up.
The key things you need to do are set up a transitioning delegate, a custom presentation controller, and a custom animation controller.
you could use a third party library, you can search them in github.comor cocoacontrols.com as navigation Drawer
In my case I use this
https://github.com/CosmicMind/Material#NavigationDrawer
others
https://www.cocoacontrols.com/search?q=Drawer
https://github.com/dekatotoro/SlideMenuControllerSwift
https://github.com/jonkykong/SideMenu

View being blocked by UITransitionView after being presented [duplicate]

This question already has an answer here:
Pass touches through a UIViewController
(1 answer)
Closed 7 days ago.
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: YES completion: nil], some reason the menu side of the NC is blocked by a UITransitionView that I cannot get to disappear.
I have tried the following:
UIWindow *window = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window];
window.backgroundColor = kmain;
CATransition* transition = [CATransition animation];
transition.duration = .5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromTop;
[nc.view.layer addAnimation:transition forKey:kCATransition];
[UIView transitionWithView:window
duration:0.5
options:UIViewAnimationOptionTransitionNone
animations:^{ window.rootViewController = nc; }
completion:^(BOOL finished) {
for (UIView *subview in window.subviews) {
if ([subview isKindOfClass:NSClassFromString(#"UITransitionView")]) {
[subview removeFromSuperview];
}
}
}];
But it is very hacky, and as the rootviewcontroller of the window changes during the transition, it's a little choppy and part of the navigationcontroller and the top right corner turn black. It looks very bad.
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 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.
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.
Ive had this issue when I was setting accessibilityElements on a popover view controller. I fix it by removing assigning an array of elements.

Issue with Slide-Out Menu Navigation In Swift

I followed This tutorial and achieve that animation but now I want to add some functionality into it like when user click in the minimised viewController I want to popup that minimised viewController back I tried to Implement TapGesture on that view and this is my code:
import Foundation
import UIKit
class TransitionOperator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate{
var snapshot : UIView!
var isPresenting : Bool = true
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
if isPresenting{
presentNavigation(transitionContext)
}else{
dismissNavigation(transitionContext)
}
}
func presentNavigation(transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let fromView = fromViewController!.view
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let toView = toViewController!.view
let size = toView.frame.size
var offSetTransform = CGAffineTransformMakeTranslation(size.width - 120, 0)
offSetTransform = CGAffineTransformScale(offSetTransform, 0.6, 0.6)
snapshot = fromView.snapshotViewAfterScreenUpdates(true)
//TapGesture for detect touch
let aSelector : Selector = "singleTap"
let tapGesture = UITapGestureRecognizer(target: self, action: aSelector)
tapGesture.numberOfTapsRequired = 1
self.snapshot.addGestureRecognizer(tapGesture)
container.addSubview(toView)
container.addSubview(snapshot)
let duration = self.transitionDuration(transitionContext)
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.8, options: nil, animations: {
self.snapshot.transform = offSetTransform
}, completion: { finished in
transitionContext.completeTransition(true)
})
}
func singleTap(){
NavigationViewController().dismissViewControllerAnimated(true, completion: nil)
println("Touched")
}
func dismissNavigation(transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let fromView = fromViewController!.view
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let toView = toViewController!.view
let duration = self.transitionDuration(transitionContext)
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: nil, animations: {
self.snapshot.transform = CGAffineTransformIdentity
}, completion: { finished in
transitionContext.completeTransition(true)
self.snapshot.removeFromSuperview()
})
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = true
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = false
return self
}
}
when I click on that minimised view Touched is print as you can see into image:
But view is not dismissed.I want to popup TimelineViewController back.
Thanks in advance.
Maybe what you need to do is call self.dismissNavigation() from your singleTap method. Though I'm not sure what context to pass to that method...
I know it's been a few months but i figured it out. Look at the function animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) which has the parameter presented, the UIViewController of the snapshot. That is your reference that you need instead of calling NavigationViewController(). which is just making new instances. So create a UIView variable var foo : UIView! and in animationControllerForPresentedController() set foo = presented.
Now in your function singleTap() you can set foo.dismissViewControllerAnimated().
Hope this helps.

Resources