Programmatically created back button does not show up in navigation bar? - ios

To make this simple, because of the app design I cannot use an embedded navigation controller and must instead use a manual back button on the view controller to move back to the previous one. As far as I know, the previous view controllers push to this one programmatically as well. So I found some great code for a button on many different questions here, and I wrote it for this app. Problem is, when I run the program the Bar Button Item I created does not show up. The code shows no errors and I am confused as to why nothing is showing up. I'll include images and the code to make this as clear as possible. As always thank you for any help you can provide!!!
Code Used:
#IBOutlet weak var roomImage: UIImageView!
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var roomName: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
// Back Button Code
let backButton = UIBarButtonItem(title:"Back",style: UIBarButtonItemStyle.plain, target: self, action: #selector(DetailView.back(sender:)))
self.navigationItem.leftBarButtonItem = backButton
navigationItem.rightBarButtonItem = backButton
navigationItem.hidesBackButton = false
// Hide da tab bar
self.tabBarController?.tabBar.isHidden = true
// Do any additional setup after loading the view.
// Enter data from other view controllers
//roomName.title = stringPassed
roomImage.image = imagePassed
locationLabel.text = locationPassed
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
// Back Button Action
func back(sender: UIBarButtonItem) {
if let navigationController = self.navigationController {
navigationController.popViewController(animated: true)
}
}
}
Actual Storyboard with Navigation Bar
When the app is run
Could it be I didn't make something an outlet or something of that sort? Thank you!!
Storyboard layout
Tree Model of Detail View (The one with the navigation bar issue)
EDIT: Still not working but the links and ideas you gave have given me plenty of new ideas and options to explore until a more concrete answer presents itself. Thank you!!!

Remove the NavigationBar that you have drag from the Controls Library panel to your ViewController. When you set the navigationItem using self.navigationItem it will not reference to that navigationBar item because it has no connection with it.
When you push your ViewController using navigationController?.pushViewController(myVC, animated: true) it will automatically show NavigationBar on the myVC screen.

Can you show how did you push to DetialView Controller
I tired programmatically
like this
func showDetialViewController(){
let detialViewController = UIViewConroller()
navigationController?.pushViewController(detialViewController, animated: true)
}

Related

Changes to navigationItem displayed with delay

I'm facing a delay when changing properties of a navigationItem inside a ViewController which is embedded in a NavigationController.
Consider the example Master-Detail-App provided by XCode: It contains a MasterViewController with a segue to a DetailViewController. I'm now trying to customize the navigation bar inside the viewDidLoad method of the DetailViewController.
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//delay if the button (or any other property) is set here
navigationItem.rightBarButtonItem = editButtonItem
}
}
The changes to the navigation bar will be displayed but with a noticeable delay. However, if the changes to the navigation bar are made from within the MasterViewController everything works perfectly fine and smooth:
class MasterViewController: UIViewController {
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
//no delay if the button would instead be set here
//navigationItem.rightBarButtonItem = editButtonItem
}
}
}
To me it seems not to be a good practice to modify items of the navigationBar from within the MasterViewController. Especially because
items like the title and buttons heavily depend on the DetailViewController.
What is now the most preferable way to change the navigationItem without delay and with separation of concern in mind?
Add code to viewWillApear instead of viewDidLoad
override func viewWillApear() {
super.viewWillApear()
navigationItem.rightBarButtonItem = editButtonItem
}

Swift Hide Button In Another View Controller

Currently working on my first IOS application. I have a purchase button, on success this currently sets a test button on the same view controller to hidden. Code is as follows
Decleration
#IBOutlet weak var Test: UIButton!
hide button on successful purchase
Test.isHidden = true
Now this works on my Test button, which is sat in the PurchaseViewController,class is the MasterViewController.Swift. (Purchase button that initiates this method is also in the same view controller)
PlanViewController also has a button, and class is also linked to MasterViewController.Swift. This has a separate button that i wish to hide on success of the purchase button.
When I utilise the same code as above for the button, it crashes, is their a limitation on manipulating other view controllers while you are not in it? I would have thought this worked given that they both have the Masterviewcontroller.swift as the class
Thanks
Although sometimes possible, it's generally not a good idea to directly manipulate one view controller's view from another view controller, as you are trying to do. Here is how I would do what you are trying to do.
First, set a segue identifier between your two view controllers by clicking on the segue in the storyboard and going to the attributes inspector. I suggest goToMasterViewController
In both MasterViewController.swift and PurchaseViewController.swift declare a variable var buttonHidden = false
In PurchaseViewController.swift add the following code, which will be called just before your segue to MasterViewController is performed:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "goToMasterViewController") {
let destinationController = segue.destination as! MasterViewController
destinationController.buttonHidden = buttonHidden
}
}
When you hide the button in PurchaseViewController, also set buttonHidden = true
And finally in MasterViewController.swift:
override func viewWillAppear(_ animated: Bool) {
testButton.isHidden = buttonHidden
}

Bar button action on ViewController in ViewContainer

I have a container embedded in a ViewController with a navigation bar. The ContainerView contains another ViewController with some textfields.
When the ContainerView is first displayed, the textfields are disabled.
What I would like to do is add an edit button to the navigation bar which enables the textfields.
Basically, is it possible for a bar button item in a viewcontroller to have an action on another view controller displayed in a container?
in this case you can do a little trick
the bar button will be in the ViewController but we can store the content of the ContainerView in a variable in the first ViewController
to do that i would suggest to make the content of the ContainerView has a custom class so you would write a function in it
here is an example on how to catch the content of the ContainerView
class ViewController: UIViewController{
weak var containter: ContainerViewController!
#IBAction func menuAction(_ sender: UIBarButtonItem) {
//do what you want containter.doAction()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let vc = segue.destination as? ContainerViewController {
self.containter = vc
}
}
}
Always the case. I've been looking for an answer for hours, as soon as I ask I figure it out.
I'm not sure its the best way to do it but I just made an edit bool controller by the edit button. Then used:
var child = self.childViewControllers[0]
child.viewWillAppear(false)
This reloaded the data and if it was in edit mode enabled the textfields. Works fine!

Remove "Back" text from navigation controller [duplicate]

This question already has answers here:
Removing the title text of an iOS UIBarButtonItem
(39 answers)
Closed 7 years ago.
I do not want to see the "Back" text that is displayed automatically when going to a view controller with no Title. I would like this to happen everywhere on my application, is there a few lines of code I can put in the app delegate to make this possible?
I have tried a few approaches from SO before posting this question and have found no success. I want to this in Swift, not Obj-c.
I tried this with no success; it ran fine, but the back text was still displayed in the next view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let backItem = UIBarButtonItem()
backItem.title = "Something Else"
navigationItem.backBarButtonItem = backItem // This will show in the next view controller being pushed
}
I would like to put this in app delegate so it would happen throughout my application rather than having to put that into every single swift file in my project. Anybody have any ideas?
There are several different ways to do this. I’ll present a solution that gives you a bit more flexability as you’ll want to display a button some times, hide other times, and customize at other times. This example assumes you are using Navigation Controller based UI, but can be adapted to other types.
Whenever I create an app I like to create my own UIViewController class and have all of my UIViewControllers inherit from this single view controller. If I want to customize something for all my UIViewControllers I can do this at my new super class level since all the other views inherit from that.
In my sample code below I create my custom UIViewController called MasterViewController. I have all of my UIViewControllers inherit from it: ViewController, ViewController2 & ViewController3. Read the notes in the code below to understand what’s going on.
NOTE: Make sure you check for the case where the UIViewController should not have the back button as this would be the case where there is nothing to return to. You can add this code to MasterViewController but I did not for this sample.
//=========================================================
//MasterViewController
//=========================================================
// Master View Controller that all UIViewControllers inherit from.
import UIKit
class MasterViewController: UIViewController {
// This will be title if the user doesn't change it in a subclass.
var backButtonTitle = "Default Back Title"
override func viewDidLoad() {
// This resets the default back button to always be shown unless the user calls createCustomBackButtonWithTitle from child class.
self.navigationItem.hidesBackButton = false
super.viewDidLoad()
}
func createCustomBackButtonWithTitle(customTitle: String) {
// Hide the default back button.
self.navigationItem.hidesBackButton = true
// Programmatically create custom back button.
let backButton = UIBarButtonItem(title: customTitle, style: UIBarButtonItemStyle.Plain, target: self, action: "goBack:")
self.navigationItem.leftBarButtonItem = backButton
}
#IBAction func goBack(sender: UIButton!) {
navigationController?.popViewControllerAnimated(true)
}
}
//=========================================================
//ViewController
//=========================================================
// This view controller customizes the back name and inherits the default "go back" behavior from MasterViewController.
import UIKit
class ViewController: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName")
}
}
//=========================================================
//ViewController2
//=========================================================
// This view controller customizes the back name but overrides the default "goBack" behavior from MasterViewController to do something different
import UIKit
class ViewController2: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName 2")
}
// Override the back button behavior from super class because we don't want default "back" behavior.
#IBAction override func goBack(sender: UIButton!) {
// Costume Code here
}
}
//=========================================================
//ViewController3
//=========================================================
// This view controller inherits from MasterViewController, but gets the default "back" behavior from a normal
// UINavigationController since it never calls createCustomBackButtonWithTitle to change the behavior
import UIKit
class ViewController3: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
Most people wind up doing something like #xdeleon suggested, but I highly recommend not doing that, if for no other reason that that you'll break the built-in swipe behavior to navigate backwards in the view controller stack. There are a couple answers on SO that tell you how to restore this back-swipe functionality, but as someone who's had to fix this in an existing app with 200+ view controllers, I'd like to save you a lot of pain.
If you don't mind working in storyboards, then make sure that each of your view controllers' navigation item's back button text is an empty string. However, since that's a lot to keep track of, it's marginally easier to do in code, and the only way I've found works perfectly is to create a cachedTitle instance variable on your view controller, and implement the view controller's viewWillAppear() and viewWillDisappear() methods like so:
override func viewWillAppear(animated: Bool) {
// Reset the view controller's title only if it doesn't already have one.
if (navigationItem.title == nil || navigationItem.title == "") && navigationItem.titleView == nil {
navigationItem.title = cachedTitle
}
}
override func viewWillDisappear(animated: Bool) {
if navigationItem.title != nil { // not 'if let'!
cachedTitle = navigationItem.title
navigationItem.title == ""
}
}
You can either put these methods in a UIViewController subclass that all of your view controllers extend, or, better yet, put these in custom methods in a category on UIViewController, and just call them in your view controllers' viewWillAppear() and viewWillDisappear() calls.
If you want to use something other than the back button's '<' symbol, don't add a custom back or left button; just set a custom back indicator image and mask on the navigation bar.

SWRevealViewController won't work after I go to the child of view controller and go back to the previous view controller

I'm working with SWRevealViewController. And I've fully implemented through application slide out main menu. But when I go to one VC which has a child from which I can go back, the self.revealViewController() returns nil for previous/parent VC. And button for main menu doesn't work anymore. I'm programming in Swift. Image below shows navigation with VCs. I have a UITableviewcontroller Projects and + button for adding new Project. After tap on +, new VC Add new project is presented. If I tap Cancel button, VC Projects is presented but main menu button doesn't work and I can't open main menu. Can anyone help me with this problem? Thanks for your time and help.
This is the code, beginning of the class ProjectsView where I set the SWViewController for every VC.
class ProjectsView: UITableViewController {
#IBOutlet var mainMenuBttn: UIBarButtonItem!
var projects: [String] = ["Project1", "Project3", "Project3"];
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
mainMenuBttn.target = self.revealViewController()
mainMenuBttn.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
...
You don't need a segue from add new project to your initial navigation controller. Create an IBAction for your cancel button like:
#IBAction func cancel(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
Same for the done button. As it is now, you are creating a new instance of ProjectsView every time you return from Add new project.

Resources