How can I push two VC but only presenting top VC - ios

I would like to emulate contact list in ios. When one taps + button to add contact, a new view controller is presented that gives you textfields to enter contact name and additional info for saving about that contact. Once you tap Done button, the next view controller presented already seems to be embedded in a navigation controller with the button that takes you back to the contact list. I've tried code found on here but it pushes 3 view onto navigation stack
//first attempt
var controllers = navigationController?.viewControllers
controllers?.append(secondVc)
controllers?.append(thirdVC)
navigationController?.setViewControllers(controllers!, animated: true)
//second attempt
let pushVC = UIViewController()
let backVC = UIViewController()
if let navigationController = navigationController {
navigationController.pushViewController(pushVC, animated: true)
let stackCount = navigationController.viewControllers.count
let addIndex = stackCount - 1
navigationController.viewControllers.insert(backVC, atIndex: addIndex)
}
I've also tried other combinations that look cringy. Here is what I want it to look like: https://imgur.com/a/IAJ5G

This should work:
navigationController.pushViewController(secondVc, animated:false)
navigationController.pushViewController(thirdVc, animated:true)

Apple does the same by presenting a new window, with the form to create a new contact.
After making the new window as the keyWindow, they push the contact details VC on to the navigation controller on the original window. This push happens in the background, which the user is not able to witness.
You can view the same by attaching a debugger to the Contacts app.
Heres a screenshot of the view hierarchy. You will be able to see that there are no view controllers from the original window underneath the CNContactContentViewController.
When the user taps Done, the original window is restored as the keyWindow, and the contact details VC is updated to show the newly added contact.

Related

Navigation bar won't show after pushing a view controller

I'm trying to move from a View Controller to another.
I worte this function to use when the user tap on a button to move to the new view controller:
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
The problem is that the new view controller is presented on the screen but I don't have a navigation bar and a back button to move back.
I do not use Storyboard as I want to learn coding the UI.
I tried few things I found here on Stackoverflow but none worked for me.
How can I set the new view controller to have a navigation bar with back button?
UINavigationController has a variable isNavigationBarHidden
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
You need to push the view Controller. Try this
let aboutVC = AboutVC()
self.navigationController?.pushViewController(aboutVC, animated: true)
You don't need to write any code.
Select the Root Navigation Controller the will control the app. In the Inspector Bar, select Simulated Metrics ( The Third Selection from the Right in the Inspector) and Check the Box " Is Initial View Controller". Then connect the next View controller which will be in essence the Landing page for the app. Once you connect other View Controllers to that View controller via a button for instance ( Select the button, then press Control Key + Drag to View Controller, select Show) , you will see the navigation Bar with "Back" displayed. Once that's done, you can add other view controllers and connect them from the landing page view controller and the Navigation Bars will be displayed.
For navigation to be visible and of use in an app , first you need to set up a Navigation controller with a Root view controller i.e your first controller and from there you can use push method on your navigation controller object to push a controller on to the stack.
For eg
let navVC = UINavigationController.init(rootViewController: YourFirstViewControllerObject())
navVC.pushViewController(NewViewControllerObj(), animated: true)

How to programmatically open certain tab of a view controller

I am writing a code in which I have a view controller with certain tabs. Upon the dismissal of a an alert, I want open 2nd tab automatically. For this purpose I posted a local notification when the alert this dismissed. The observer for this notification is in home view controller, when this observer is called, 2nd tab of the view controller is selected. Here is my code for a better understanding:
vc.dismiss(animated: true, completion: {
// vc is the view controller in which my custom alert is shown
NotificationCenter.default.post(name: NSNotification.Name.OpenConsumerTab,
object: ConsumerHomeTab.Stats.rawValue)
})
And home view controller: (question related code)
NotificationCenter.default.addObserver(self,
selector: #selector(onOpenConsumerTabNotificationRecieved(notification:)),
name: Notification.Name.OpenConsumerTab,
object: nil)
#objc public func onOpenConsumerTabNotificationRecieved(notification: Notification) {
if (notification.object as! Int == ConsumerHomeTab.Stats.rawValue)
{
selectedIndex = 1
}
}
This is opening the tab but the output I am obtaining is this: (getting an extra black bar)
Why is this happening? Maybe it is showing up a completely new home view controller (with tab bar) but what is the reason? Correct me if I am wrong as I am newbie to iOS.
Things I have tried:
Get the root view controller (home view controller) and select its
tab but the output was same.
Using pop view controller but it opens up first tab (index 0) but my
requirement is to open the second tab (index 1)
I think there is no need of using Notification center too, u can write this code when you are dismissing that VC
to dismiss your alert
alert.dismissWithClickedButtonIndex(-1, animated: true)
to change View-Controller:
self.tabBarController?.selectedIndex = selectedTabIndex
// selectedTabIndex is the index of ViewController which you want to select
Please use tabbarController instance to set selected index as, may be you not using tabbar controller instance to set index :
if let tababarController = self.window!.rootViewController as! UITabBarController? { tababarController.selectedIndex = 1
}

Present VC modally above other VC - both touchable

I want to show a VC above another VC but want both to be clickable and responsible.
I have a textfield which prints out the string behind each #-sign.
When a # is written I have a VC which pops up with a small tableview above the first VC which should later represent the users who have a name containing the printed out string.
I need the first vc to still be responsible, because currently I can't click buttons and textfields on the first VC when the second one pops up even though I can see everything ( set the background to clear).
if commentTextField.text!.contains("#"){
print("changed textField")
let vc = self.storyboard?.instantiateViewController(withIdentifier: "suggestedUsersViewController") as UIViewController!
vc?.modalPresentationStyle = .overCurrentContext
vc?.view.backgroundColor = .clear
present(vc!, animated: true, completion: nil)
}
How I get the post behind the #- sign
let caption = commentTextField.text
let words = caption?.components(separatedBy: CharacterSet.whitespacesAndNewlines)
for var word in words! {
if word.hasPrefix("#") {
word = word.trimmingCharacters(in: CharacterSet.punctuationCharacters)
print("wort ist", word)
}
}
Currently it just pops over the first vc and then its no longer clickable and I cannot change the textfield
Pictures
present function will make the second view controller cover the whole page and you will no longer have access from screen to the view controller below the top one.
To accomplish your goal, you need to put a UITableView on a view, then keep showing or hiding the view. If you really want another view controller, you can create a containing view and have a embed segue to another view controller, like this

swift3 how to show navigation viewcontroller

I would like to open the specific view from the notification widget.
normally other answers are create new view..(let viewcontroller = ... )
so If you continue to call, continue use memory..
So, I want to open viewA, which is already opened in my app, and then move to viewB.
My application structure is basically a viewA when the application is launched.
What I think is that instead of creating a new viewA, I want to move to viewB using the navigation.push from the already existing viewA.
The way to check whether a particular page already exists, how to show that page at the top , and how to navigation.push is work.
now i'm using this code to open viewA from Appdelegate
let MainView: Main_View_List_ = mainStoryboard.instantiateViewController(withIdentifier: "Main_List_View") as! Main_View_List_
let nav = UINavigationontroller.init(rootViewController: MainView)
UIApplication.topViewController()?.present(nav, animated: true, completion: nil)
Assuming you have access to the navigation controller, you'll want to do something like this to pop back to viewA:
for viewController in nav.viewControllers {
if viewController is ViewAClass {
viewController.pushViewBFlag = true
nav.popToViewController(viewController, animated: true)
return
}
}
Once you get back to viewA, you can check for pushViewBFlag and create and push viewB.
Alternately, since it seems that you are setting viewA as the root of your navigation controller, you could do: nav.popToRootViewControllerAnimated(true) to get back to viewA. You would need to then handle pushing viewB from viewA.

Clearing Navigation Stack in Swift

I'm currently designing an application that has a funnel flow and a dashboard flow. I'd like the funnel flow to be cleared from memory on completion:
So if it goes
1) if new user start funnel -> 2) funnel screens 1-5 -> 3) funnel complete screen
I'd like to transition to dashboard screen which is not yet on the stack (it's not my head controller in this case). How can I clear the 6 screens above from memory when I transition to dashboard - basically setting a new navigation root and clearing the rest? An unwind segue doesn't appear to be able to set a new root destination.
If you only want to clear the navigation stack, and push a new view on top of it, there is an even simpler solution.
Let's suppose you already (programmatically) assigned a navigation controller, e.g. in a viewDidLoad() function, like:
let navController = UINavigationController( rootViewController: YourRootController )
view.addSubview( navController.view )
addChildViewController( navController )
navController.didMoveToParentViewController( self )
YourRootController acts as the stacks's root (the bottom of the stack).
To push other controllers on top of the stack (your funnel controllers), simply use navController.pushViewController( yourControllerInstance!, animated: false ).
If you want to clear the stack after completion of the funnel, just call:
navController.popToRootViewControllerAnimated( false )
This function removes all views (besides the root controller) from your stack.
So I ended up having to do it programmatically by popping back to the first controller and then replacing from the funnel head to the dashboard head:
func bookingCompleteAcknowledged(){
//remove the popup controller
dismissViewControllerAnimated(true, completion: nil)
//remove current controller
dismissViewControllerAnimated(true, completion: nil)
if let topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
if let navcontroller = topController.childViewControllers[0] as? UINavigationController{
navcontroller.popToRootViewControllerAnimated(false)
if let funnelcontroller = navcontroller.childViewControllers[0] as? FunnelController {
funnelcontroller.removeFromParentViewController();
funnelcontroller.view.removeFromSuperview();
let revealController = self.storyboard?.instantiateViewControllerWithIdentifier("DashboardController") as! SWRevealViewController
navcontroller.addChildViewController(revealController)
navcontroller.view.addSubview(revealController.view)
}
}
}
}
I believe you can do this by simply assigning a new array (with just the dashboard view, in your case) to the UINavigationController's viewControllers property.
Why do you want to use the same navigation controller instead of making a new one? Generally, instead of trying to change the root of a navigation controller, I would recommend just creating a new navigation controller.

Resources