Disabling segue animation - ios

I want to Show (e.g. push) segues in my storyboard, to connect my viewcontrollers and my navigation controller. Then the navigation bars on the viewcontrollers will show correctly.
For example: With show detail or present modaly, the navigation bar will disappear
But I don't want segue animation.
Xcode is giving the warning like : "Disabling segue animation is not available prior to iOS 9.0"
And I wants deployment target of iOS 7.0 or 8.0
How can I solve this?
Thanks in advance.

You can disable animations before performing the segue and after enable it again.
UIView.setAnimationsEnabled(false)
self.performSegueWithIdentifier("next", sender: nil)
UIView.setAnimationsEnabled(true)
This will perform the segue without the animation.

Click on segue arrow in Main.Storyboard and then:
Check out Animates

If you want to switch animate state in the code, You can duplicate your segue in the storyboard, with different identifiers, and the same origin and destination. Then make one of theme animates and the other not. Then, do performSegue with the desired identifier.
class MyNavigationController : UINavigationController {
var firstTransitionAnimated : Bool = true // or false, based on initialization
override func viewDidLoad() {
super.viewDidLoad()
var properSegue = firstTransitionAnimated ? "animated_segue" : "not_animated_segue"
self.performSegue(withIdentifier: properSegue, sender: self)
}
}

I made a custom segue, using the Swift answer in this thread:
Push segue in xcode with no animation
So:
class ShowNoAnimationSegue: UIStoryboardSegue {
override func perform() {
let source = sourceViewController as UIViewController
if let navigation = source.navigationController {
navigation.pushViewController(destinationViewController as UIViewController, animated: false)
}
}
}
And in Xcode, in the Attributes Inspector of the custom Segues, I have checked the 'Animates' box (YES). Now the warning is gone, so that is why I am answering my own question.
I am not really sure yet if it is a durable solution.

Related

Swift 3 : Switch ViewControllers programmatically

I am creating an application which shows a PageVC: UIPageViewController as intro and guide to app.
After navigating through the intro, a Next Button leads to "SettingsVC" which is used to store default settings for the app.
Settings have to be chosen by the user initially although can be changed later.
PageVC ---> SettingsVC
A Save and a Cancel button on the SettingsVC leads to the MainVC of the app.
A button on MainVC leads to SettingsVC.
SettingsVC <---> MainVC
The app would work as follows:
if isFirstLaunch{
instantiate PageVC
}else{
instantiate MainVC
}
in PageVC
nextButtonPressedinPageVC{
instantiate SettingsVC
}
in SettingsVC
if saveButtonPressed && cameFromPageVC{
instantiate MainVC
}
if cancelButtonPressed && cameFromPageVC {
do Nothing
}
if saveButtonPressed && cameFromMainVC{
dismiss currentVC
}
if cancelButtonPressed && cameFromMainVC {
dismiss currentVC
}
in MainVC
if settingsButtonPressedinMainVC {
instantiate SettingsVC
}
I have made sure that if it is application's first launch, PageVC will be instantiated else MainVC will be instantiated.
How can I move between the viewControllers without a possible memory leak i.e. where to performSegue and where to dismiss current VC?
Please include code for reference.
There are many ways to do this, here is one that I find very straightforward because you can do most of the work in your Storyboard:
Think of your MainVC as the rootViewController and the other two as accessory views that will only temporarily be shown. The MainVC should always be your entry point, so set it as the initial VC in your Storyboard.
The other two should be displayed modally so that you can easily return to the MainVC by dismissing them, no matter how you opened them in the first place.
To do this, draw a segue from your MainVC button to the PageVC and name it "showPageVC". From the Next button in your PageVC, draw another segue to the SettingsVC. Now you need some code to handle the dismiss actions: put this snippet in your MainVC:
#IBAction func unwindToMain(segue:UIStoryboardSegue) {
}
This function is just a marker, so it doesn't need a body. It just enables you to create a unwind segue back to MainVC: For each of the buttons in SettingsVC, hold Ctrl and draw from the button to the right exit icon in the header of the SettingsVC storyboard scene and choose unwindToMain in the tiny black popup.
Now you only have to implement the logic to decide if you want to show the PageVC or not in viewDidAppear() of the MainVC. So the whole code would look something like this:
class MainVC: UIViewController {
var didDisplayPageVC = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if (didDisplayPageVC == false) {
performSegue(withIdentifier: "showPageVC", sender: self)
didDisplayPageVC = true
}
}
#IBAction func unwindToMain(segue:UIStoryboardSegue) {
}
}
The rest is in the storyboard. If this little proof-of-concept is working, you can go and configure the segues (you might want to remove the animation) etc.

An alternative to "Storyboard Reference" for iOS 8 when handling Relationship Segue?

My App has a TabBarViewController containing 4 tabs. One of the tabs is Settings which I want to move to a separate storyboard. If I am only consider iOS 9 and above as my deployment target, then I can just refactor the SettingsTab using Storyboard Reference. However I want to target iOS 8 as well. Since Storyboard Reference doesn't support Relationship Segue, I can't rely on it in this case.
So in the main storyboard which contains the TabBarViewController, I keep a dummy SettingsTabViewController as an empty placeholder. And in the function "viewWillAppear" in its class file, I push the view to the real SettingsTabViewController in the Settings.storyboard. This works fine. But the problem is if I keep tabbing the Settings tab, the empty placeholder view controller will show up for a short time and then goes back to the real Settings view.
I tried to implement this delegate to lock the Settings tab:
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
return viewController != tabBarController.selectedViewController
}
However, the other three tabs were locked too after I implemented this delegate.
Is it possible to just lock the Settings tab without locking other three tabs? And in which view controller exactly should I implement this delegate?
Yes, it's possible. You need to check the index;
with the following code not only you can prevent locking other tabs, but also you still have tap on tab goto root view controller feature.
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
let tappedTabIndex = viewControllers?.indexOf(viewController)
let settingsTabIndex = 3 //change the index
if tappedTabIndex == settingsTabIndex && selectedIndex == settingsTabIndex {
guard let navVC = viewController as? UINavigationController else { return true }
guard navVC.viewControllers.count > 1 else { return true }
let firstRealVC = navVC.viewControllers[1]
navVC.popToViewController(firstRealVC, animated: true)
return false
}
return true
}
.
This answers your question, but still you would have the settingsVC showing up for a moment. To avoid this you simply need to turn off the animation while you're pushing it. so you need to override viewWillAppear in the following way.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if let theVC = storyboard?.instantiateViewControllerWithIdentifier("theVC") {
navigationController?.pushViewController(theVC, animated: false)
}
}
after adding above code you still would see a back button in your real first viewController. You can hide it:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true
}

Dismissing a Modal ViewController back to a Navigation Controller Stack imbedded in a TabBarController causing crash

Have been working for a long time trying to find the fix to my crash. Not sure if it's a fundamental design flaw on my part or something that can be fixed easily.
My app has a NavigationController embedded in a TabBarController. When I modally present a view from the root view controller of the NavigationController, it is presented and dismissed without any problem.
However, after i navigate forward in the navigation stack from the root ViewController of the NavigationController pushing other Controllers and then back to the root ViewController, I try the modally presentation again. This time it presents fine but when I dismiss it my app crashes with a EXC_BAD_ACCESS error.
After sometime debugging I found that when the modally presented ViewController is dismissed it is trying to access a ViewController in the navigation Heiarchy that has already been dismissed and thus the crash. Despite the fact that it was presented from the Root ViewController of the Navigation Stack.
It is causing me a lot of grief and wondering if anybody has come across something similar. Any help will be greatly appreciated. Thanks in advance.
Here is the presentation which occurs inside an AlertController Block:
let createNewMemoAction = UIAlertAction(title: "Create New Memo", style: .Default) { (action: UIAlertAction!) -> Void in
self.performSegueWithIdentifier("Segue To Create New Memo", sender: nil)
}
I am using an unwind to dismiss the viewController. I have tried dismissing with the following code with the same error:
#IBAction func cancelButtonPressed(sender: UIBarButtonItem) {
dismissViewControllerAnimated(true, completion: nil)
}
Thanks
did you implement the prepareForUnwind function in the presenting view controller? you need that in order for the unwind to work: here is an example in objective C but it's the same concept for swift!
This code goes in the presenting view controller:
#IBAction func backToHome(segue:UIStoryboardSegue) {
// code is not necessary here!
}
instead of writing code for dismissing the viewcontroller you just need to ctrl + drag from the button you use for dismissing to the exit at the top of the scene and then choose the function "backToHome" from the popup
you should check the connections:
https://www.dropbox.com/s/jwpaz1nxvjkautv/Screen%20Shot%202015-07-12%20at%206.16.23%20PM.png?dl=0
I recommend you do push segue and make your own protocol, that way you can navigate back to any viewcontroller. For your example, the pushed viewcontroller code I wrote is :
import UIKit
protocol BoxMessageDelegate {
func userDidType(controller: TextAddition, sendsmessage: String) -> Void;
}
class TextAddition: UIViewController {
var delegate: BoxMessageDelegate?;
var message: String?;
#IBOutlet weak var textMsgField: UITextField!
#IBAction func `return`(sender: UIButton) {
self.message = self.textMsgField.text;
[self.delegate!.userDidType(self, sendsmessage: message!)];
}
}
in the presenting view controller code is:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "MessageView") {
var controller = segue.destinationViewController as! TextAddition
controller.delegate = self
}
}
/*
conforming to protocol here
*/
func userDidType(controller: TextAddition, sendsmessage: String) {
mySquare.text = sendsmessage;
self.navigationController?.popToViewController(self, animated: true)
}
I found the problem. I had declared the second view in the NavigationController Hierarchy as the delegate for the NavigationController. That's why it was being referenced. When I removed that line of code the problem went away. Thanks very much for all who commented and helped out.

Swift - Hiding Tab bar in PageViewContoller scenes

I set up a series of onboarding scenes for my app using this tutorial, and elsewhere in my app use a TabViewController. When I run the app, the Onboarding scenes have a blank tab bar at the bottom.
I've tried ticking "Hide Bottom Bar on Push" from all 3 views on interface builder:
I've also tried adding
self.hidesBottomBarWhenPushed = true to the override func viewDidLoad() functions of the ViewController, PageViewController and PageContentViewController. No joy!
Also tried self.tabBarController?.tabBar.hidden = true in the same places.
Elsewhere I've found references to this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showLogin"{
println("showLogin seque called")
let bottomBar = segue.destinationViewController as LoginViewController
bottomBar.hidesBottomBarWhenPushed = true
bottomBar.navigationItem.hidesBackButton = true
}
}
but, I don't have any named segues, so not sure how to identify the segue.
Any help would be amazing!
Thanks
To give the Segue name, first choose the segue and then go to Attributes Inspector.
Below there you find Storyboard Segue, from there you provide Identifier name in Identifier Box.
Thanks

Calling segue programmatically not working

I am trying to make a splash screen. I have a view that has a background image being drawn onto it and then another view I want to transition to after a few seconds. I am trying to use the following code:
self.performSegueWithIdentifier("showApp", sender: self)
I created a segue between the two views by ctrl+dragging a line from one to the other. I set the segue's identifier to "showApp".
When I run the code nothing happens and there are no errors. Any ideas?
Here is the controller for the splash screen:
class SplashViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
sleep(2)
// THIS DOES NOTHING:
self.performSegueWithIdentifier("showApp", sender: self)
// THIS AS SUGGESTED ALSO DOES NOTHING:
var otherViewController:UIViewController = self.storyboard.instantiateViewControllerWithIdentifier("test") as UIViewController
self.presentViewController(otherViewController, animated: true, completion: nil)
}
}
Normally, you need a navigation controller in order to use segue.
Highlight your SplashViewController object inside the Storyboard and go to
Editor -> Embeded In -> Navigation Controller
After that, remove the code suggested by Clement and try running the project again, you should get the result that you expected.

Resources