User interaction disabled for parent viewController when removed from child ViewController - ios

I am using below code to move from left to right.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.rightNavController?.view.frame = CGRect(x: 0, y: 0, width: (appDelegate.window?.frame.size.width)!, height: (appDelegate.window?.frame.size.height)!)
self.removeNavigationController()
appDelegate.rightNavController = UINavigationController(rootViewController: leftmenuViewController)
appDelegate.rightNavController?.navigationBar.isHidden = true
baseController.addChildViewController(appDelegate.rightNavController!)
baseController.view.addSubview((appDelegate.rightNavController?.view)!)
UIView.animate(withDuration: 0.25, delay: 0.0, options: UIViewAnimationOptions(), animations: {
}, completion: { finished in
appDelegate.rightNavController?.view.frame = CGRect(x: 0, y: 0, width: 320, height: 568)
})
my view hierarchy in side menu is UIView->UiTableView.I have added tap gesture on UIView to dismiss side menu.
Below is the code used to dismiss:(tap gesture method)
self.willMove(toParentViewController: nil)
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions(), animations: {
self.view.frame = CGRect(x:-self.view.frame.size.width, y: 0 ,width : self.view.frame.size.width, height :self.view.frame.size.height)
}, completion: { finished in
self.removeFromParentViewController()
self.view.removeFromSuperview()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.rightNavController = nil
appDelegate.rightNavController?.view = nil
})
Now the issue is when child viewController is removed, parent view controller user interaction is disabled.
Any help to solve this issue.

It's not entirely clear what you're trying to achieve from the wording of the question but I would look for the isUserInteractionEnabled property on the view that's causing you trouble.
Also, maybe have a look to yourView.layer.zPosition. The view getting the interaction is the one with the highest zPosition value. Everyone defaults to zPosition = 0. All else being equal, the order in which the views where added will dictate who gets the events.

Related

How to use completion block for UIView.animate()?

I'm working on a project to learn animations and am having trouble using the completion block for the UIView.animate(withDuration:) func. MY animation is a shoebox that falls from the top of the screen, lands on a pedestal, then opens. Once the box opens I want a UIImageView to come out of the box, grow to full size of the screen and then segue to the next page, but the completion handler that the code for segueing is called before my animation completes and the UIImageView doesn't appear at all.
Here's my code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseInOut,
animations: {
//Opening the box
self.shoeBoxImage.shoeBox.animationImages = self.boxOpeningAnimation
self.shoeBoxImage.shoeBox.animationDuration = 1.5
self.shoeBoxImage.shoeBox.animationRepeatCount = 1
self.shoeBoxImage.shoeBox.contentMode = .scaleAspectFill
self.shoeBoxImage.shoeBox.startAnimating()
//set to the final image
self.shoeBoxImage.shoeBox.image = UIImage(named: "frame13")
},completion: {_ in
let nextPage = UIImageView()
nextPage.frame = CGRect(origin: self.shoeBoxImage.center, size: CGSize(width: 0.0, height: 0.0))
nextPage.image = UIImage(named: "FirstLoadBackgroundImg.jpeg")
nextPage.autoresizesSubviews = true
self.view.addSubview(nextPage)
self.view.bringSubviewToFront(nextPage)
UIView.animate(withDuration: 5.0, animations: {
nextPage.transform = CGAffineTransform(scaleX: 428, y: 926)
})
self.performSegue(withIdentifier: "FinishedLoading", sender: self)
})
}
This is my first time working with animations and programatically creating views so if someone could explain how to make the completion block wait for the animation to complete. In order to make the UIImageView appear and animate then once it's full screen, segue to the next page it would be very much appreciated.
The size is 0, 0. Transforming zero by any scale is still zero. I would advise you to not use transform at all, but rather just set the final frame to be what you want.
E.g.,
let startFrame = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
let endFrame = view.bounds
let imageView = UIImageView(image: ...)
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.frame = startFrame
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
imageView.frame = endFrame
} completion: { _ in
// do something here
}
That yields:
By the way, the performSegue probably should be inside a completion closure of the inner animate call.

Adding a subview inside of a UIStoryboardSegue

I'm writing an app where I need to use a fairly "complex" UIStoryboardSegue. The designer gave me the following:
Now the basic idea behind the segue is simple, sliding up the source.view, then sliding up one more view before eventually sliding the destination.view. However, my question is the following:
How do I insert a second view in-between the source.view and the destination.view from a subclass of UIStoryboardSegue ? Obviously I can't just do addSubview, since there is no view to add to. Is there another place to add a view in a UIStoryboardSegue, so I can still create this segue?
Thanks.
If this is what you want
You can achieve it very easily,
Step 1:
Create a subclass of UIStoryboardSegue and override perform
import UIKit
class SpecialEffectSegue: UIStoryboardSegue {
override func perform() {
let firstVCView = self.source.view as UIView!
let secondVCView = self.destination.view as UIView!
let intermediateView = UIView()
intermediateView.backgroundColor = UIColor.red
// Get the screen width and height.
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
// Specify the initial position of the destination view.
secondVCView?.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
intermediateView.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
// Access the app's key window and insert the destination view above the current (source) one.
let window = UIApplication.shared.keyWindow
window?.insertSubview(intermediateView, aboveSubview: firstVCView!)
window?.insertSubview(secondVCView!, aboveSubview: secondVCView!)
UIView.animate(withDuration: 0.4, animations: { () -> Void in
firstVCView?.frame = ((firstVCView?.frame)?.offsetBy(dx: 0.0, dy: -screenHeight))!
intermediateView.frame = (intermediateView.frame.offsetBy(dx: 0.0, dy: -screenHeight))
}) { (Finished) -> Void in
UIView.animate(withDuration: 0.4, animations: { () -> Void in
secondVCView?.frame = (secondVCView?.frame.offsetBy(dx: 0.0, dy: -screenHeight))!
}) { (Finished) -> Void in
self.source.present(self.destination, animated: false, completion: {
intermediateView.removeFromSuperview()
})
}
}
}
}
Though code looks huge and complicated what is happening in it is pretty simple.
Get the source and destination viewController's view using
let firstVCView = self.source.view as UIView!
let secondVCView = self.destination.view as UIView!
Because you need a intermediate view which is red in color here you create one more view
let intermediateView = UIView()
intermediateView.backgroundColor = UIColor.red
Now get the UIScreen width and heght so that you can configure these views frame so that it looks good as per your need
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
// Specify the initial position of the destination view.
secondVCView?.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
intermediateView.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
Note that the I set the frame of SecondVC and intermediateView such that they are below screen bounds and Ill animate them to come up in UIView.animate block
Now obviously because animations are happening on key window of your app access the key window
let window = UIApplication.shared.keyWindow
Now insert the subviews to window as per your need.
window?.insertSubview(intermediateView, aboveSubview: firstVCView!)
window?.insertSubview(secondVCView!, aboveSubview: secondVCView!)
So after this I have views stacked up as FirstVCView -> IntermediateView -> SecondVCView
Now we have pretty much what we need isnt it. Now animate it using UIView.animate
UIView.animate(withDuration: 0.4, animations: { () -> Void in
firstVCView?.frame = ((firstVCView?.frame)?.offsetBy(dx: 0.0, dy: -screenHeight))!
intermediateView.frame = (intermediateView.frame.offsetBy(dx: 0.0, dy: -screenHeight))
}) { (Finished) -> Void in
UIView.animate(withDuration: 0.4, animations: { () -> Void in
secondVCView?.frame = (secondVCView?.frame.offsetBy(dx: 0.0, dy: -screenHeight))!
}) { (Finished) -> Void in
self.source.present(self.destination, animated: false, completion: {
intermediateView.removeFromSuperview()
})
}
}
Important thing to notice is intermediateView.removeFromSuperview() in completion block.
Now I have decided to present destination using self.source.present( if you need to push destination VC with your funky animation say
self.source.navigationController?.pushViewController(destination, animated: false)
Thats all :)
Now open your storyboard, drag a segue from one your FirstVC to SecondVC and select the segue class as SpecialEffectSegue thats it now enjoy
Hope it helps :)

UIView animation that mimics presentViewController easing

I have a view which animates from the bottom of my ViewController, and I'm trying to get the animation to be the same as the default
present(viewController, animated:true, completion:nil)
I want to animate a view with the same easing and timing as the above code. Right now I'm using this:
UIView.animate(withDuration: 0.35, delay: 0, options: .curveEaseInOut, animations: {
self.myView.frame = newFrame
}, completion: nil)
The animations aren't similar though, and I don't know what values to use in my UIView animation to make it look like a viewController present. Anyone know what values could get me close?
here is sample code with somewhat animation like presenting viewcontroller
var subViewNew = UIView()
make initial dream setup of you subview which you want to make to add on main view as below
override func viewDidLoad() {
super.viewDidLoad()
subViewNew.frame = CGRect(x: 100,y: 100,width:self.view.frame.width - 200,height:200)
self.subViewNew.frame.origin.y = self.view.frame.origin.y + self.view.frame.size.height
subViewNew.backgroundColor = UIColor.gray
}
for appearance of the view subview you can go like this
UIView.animate(withDuration: 0.7, animations: {
self.subViewNew.frame.origin.y = 300 + 20
self.view.addSubview(self.subViewNew)
})
for dismiss or hiding it you can go like this
UIView.animate(withDuration: 0.7, animations: {
self.subViewNew.frame.origin.y = self.view.frame.origin.y + self.view.frame.size.height
self.subViewNew.removeFromSuperview() //If you want then only
})
Hope this help you

UIButton not clickable while returning to one view from second view

I have an app. I assume two of the views have a problem. The first view has three buttons arranged one below the other. Clicking any of the three will take us to the second view with 3 tableview cells.
The problem is, if we go to the second view and come back to the first, the buttons in the first view are not clickable. I am attaching the screens of two views.
View 1: There are 3 buttons.
View 2: These are table view cells.
Note: The table view cells are clickable. We go back to the first view by pressing a close button. in the top left corner of the second view.
**Problem in a nutshell: If we go to view 2, click the 'X' close button, the three buttons in view 1, that is fb, G+ and Email are not clickable. **
Here is the code for the three buttons in the first view.
#IBAction func firstBtnAction(_ sender: Any) {
let bounds = self.firstBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.firstBtn.bounds = CGRect(x: bounds.origin.x - 20, y: bounds.origin.y, width: bounds.size.width + 20, height: bounds.size.height)
self.firstBtn.isEnabled = false
}, completion: nil)
goaltype = "Conversion"
UserGoal()
}
#IBAction func secondBtnAction(_ sender: Any) {
let boundss = self.secondBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.secondBtn.bounds = CGRect(x: boundss.origin.x - 20, y: boundss.origin.y, width: boundss.size.width + 20, height: boundss.size.height)
self.secondBtn.isEnabled = false
}, completion: nil)
goaltype = "Engagement"
UserGoal()
}
#IBAction func thirdBtnAction(_ sender: Any) {
let bounds3 = self.thirdBtn.bounds
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.thirdBtn.bounds = CGRect(x: bounds3.origin.x - 20, y: bounds3.origin.y, width: bounds3.size.width + 20, height: bounds3.size.height)
self.thirdBtn.isEnabled = false
}, completion: nil)
goaltype = "Storytelling"
UserGoal()
}
There is a small animation for the second view table view cell.
Edit 1: When I tried adding isEnabled = true, the button was clickable. However, due to animation, it keeps expanding on each click. See the pic:
How do you make the buttons clickable?
you are setting isEnabled = false for all the buttons. Enable it when coming back to the first view
self.firstBtn.isEnabled = true
self.secondBtn.isEnabled = true
self.thirdBtn.isEnabled = true
For Disabling buttons only at the time of animation, enable it in the completion block
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, options: [], animations: {
self.thirdBtn.bounds = CGRect(x: bounds3.origin.x - 20, y: bounds3.origin.y, width: bounds3.size.width + 20, height: bounds3.size.height)
self.thirdBtn.isEnabled = false
}) { (_ isCompleted) in
self.thirdBtn.isEnabled = true
}
Similarly do it for both firstBtn & secondBtn animations

Custom Segue Class UIView Animation Issue

I am having trouble creating a custom segue using swift 3 and iOS 8. I am trying to transition between view controllers by fading from one VC to a black screen and then fading from black to my second VC. I tried to implement the segue by creating a custom segue using the code below, but it is not working as I would like it to. My goal is to perform an animation when the black square goes from 0.5 alpha to 1.0 alpha, then present my second view controller, then set the black square from 1.0 alpha back to 0.5 alpha and delete it. Right now, it does the first part correctly but after the animation finishes you can see the first VC for a brief instant before the second VC pops up. How should I change my code to make the transition smoother and get the desired result?
override func perform() {
let sourceVC = self.source
let destinationVC = self.destination
let square = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
square.alpha = 0.5
square.backgroundColor = UIColor.black
sourceVC.view.addSubview(square)
UIView.animate(withDuration: 0.2, animations: {
square.alpha = 1.0
})
UIView.animate(withDuration: 0.2, delay: 0.2, animations: {
square.alpha = 0.5
}) { (finished) in
sourceVC.present(destinationVC, animated: false, completion: nil)
}
}
Update your code like below and try:
UIView.animate(withDuration: 0.2, animations: {
square.alpha = 1.0
UIView.animate(withDuration: 0.2, delay: 0.2, animations: {
square.alpha = 0.5
}) { (finished) in
DispatchQueue.main.async {
sourceVC.present(destinationVC, animated: false, completion: nil)
}
})
}

Resources