Can't get a back button on my view controller in Swift - ios

Here's what's going on:
I have a Navigation Controller with A-TableViewController set as root view controller. Once I click on a cell in A, it'll take me to B-ViewController. The navigation controller has an identifier "MessagesViewController". Here's my code thus far in A-TableViewController:
func tableView (tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let sb = UIStoryboard(name: "Main", bundle: nil)
let messagesVC = sb.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
//Some code here
//This has a back button, but nothing else
self.navigationController?.pushViewController(messagesVC, animated: true)
//This has no back button, but everything else that I intended works
self.navigationController?.presentViewController(messagesVC, animated: true, completion: nil)
I want to be able to go back to A-TableViewController with everything working. Is it the way I'm pushing/presenting the view controller that's messing it up? Anyone have any clue why I've been stuck on this for the past 3 days?

You get built in back button when you push a view on to a navigation view. The presentViewController is modally displaying your view. What I've done in the past is add my own back button to the view and present it. Then when you press it you call dismissViewController.

After Presenting your B-ViewController Try this in viewDidLoad:
let btnBack = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(dismissVC))
btnBack.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .normal)
navigationItem.setLeftBarButton(btnBack, animated: true)
#objc func dismissVC() {
self.dismiss(animated: true, completion: nil)
}

You can use the prepareForSegue method to pass data, something like this:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "messageSegue" {
let vc: B-ViewController = segue.destinationViewController as! B-ViewController
//then set properties of your new viewController like this
vc.property = dataToPass
}
}

If you are indeed using a navigation controller, than your problem should be quite simple.
Create an #IBAction, and in it, call popToRootViewControllerAnimated, like so:
#IBAction func rootButton(sender: UIButton) {
navigationController?.popToRootViewControllerAnimated(true) // or false :)
}

Related

Swift 5. How present or show ViewController on button touch? Without Storyboard (programmatically)

How can I show() or persent() VC on button touch? What code should I write?
First, using Storyboards
If you are working with storyboard. You should connect your button view storyboard.
#IBAction fileprivate func handlePresentingView(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}
Second, programmatically
1: If you are working programmatically
in viewDidLoad add the following line.
mybutton.addTarget(self, action: #selector(handlePresentingVC(_:)), for: .touchUpInside)
2: Your action method
#objc func handlePresentingVC(_ sender: UIButton) {
let vc = SecondVC()
present(vc, animated: true, completion: nil)
}
In my example, I'm assuming that you don't have a storyboard file for
your SecondVC view controller.
If SecondVC is connected to a storyboard view controller, you will
need to change the instantiation of your secondVC object inside of
your button's action method.
1: Select your SecondVC's view controller in your storyboard.
2: Add a Storyboard ID to it.
3: Change your button's action method to the following.
#objc func handlePresentingVC(_ sender: UIButton) {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let secondVc = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
present(secondVc, animated: true, completion: nil)
}
In Swift 5 and iOS 13
The default modal presentation style is a card. This shows the previous view controller at the top and allows the user to swipe away the presented view controller.
To retain the old style you need to modify the view controller inside of your button's action method like this:
secondVc.modalPresentationStyle = .fullScreen
This is the same for both programmatically created and storyboard created controllers.

How to transfer the data from a right button on Navigation Bar to another ViewController?

I have a Navigation Bar with two buttons: Back and Add. I need to transfer data (UIImage) to second View Controller (ViewControllerFilters2), when I click on the add button. I have the "prepare for segue" function but it doesn't work. When I click on "Add", I go to the second controller, but imageView doesn't have an image.
I also have another button "Apply" on view controller, and for this button this function works but not for button on navigation bar.
I tried to change my code, but I don't have a lot of knowledge to do it. And don't know, where is the problem.
I want my Add button do the same function as Apply button, i.e. I want delete Apply button and change it to Add button
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "✔", style: .plain, target: self, action: #selector(addTapped))
}
#objc func addTapped() {
let filterTwoController = self.storyboard?.instantiateViewController(withIdentifier: "filter2") as! ViewControllerFilters2
self.navigationController?.pushViewController(filterTwoController, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nextController = segue.destination as? ViewControllerFilters2
nextController?.filteredImage = imageView.image!
}
#objc func addTapped() {
let filterTwoController = self.storyboard?.instantiateViewController(withIdentifier: "filter2") as! ViewControllerFilters2
filterTwoController.filteredImage = imageView.image!
self.navigationController?.pushViewController(filterTwoController, animated: true)
}
that should fix your issue, because the navigationController?.pushViewController has nothing to do with the segue
In order to resolve the issue, you should do the following:
Make sure that there is a segue created (from the navigation item to the next view controller) on your storyboard. If you don't know how to do it, check this. You could add the bar button from the storyboard thus create the segue.
Currently, you are calling navigationController?.pushViewController, which is not related to the segue, what you should do instead is to call:
self.performSegueWithIdentifier ("SecondViewController", sender: self)
Change this:
#objc func addTapped() {
let filterTwoController = self.storyboard?.instantiateViewController(withIdentifier: "filter2") as! ViewControllerFilters2
self.navigationController?.pushViewController(filterTwoController, animated: true)
}
to this:
#objc func addTapped() {
let filterTwoController = self.storyboard?.instantiateViewController(withIdentifier: "filter2") as! ViewControllerFilters2
filterTwoController.filteredImage = imageView.image!
self.navigationController?.pushViewController(filterTwoController, animated: true)
}
As Ahmad F said, you are calling navigationController?.pushViewController, which is not related to the segue, so you need to set the image before you push the view controller.

My show segue not using NavigationController

I am having problems segueing to my detail view, using Navigation Controller.
I was using a tableview, to display some exercises, and when you clicked the exercise you would seque into the detail view, embedded in nav controller
I have removed the Tableview, and added 3 buttons, each with its' own "show" segue to the detail view. It almost works, but it is now not using my Navigation controller, meaning that the view is just presented kind of like a modal presentation.
I have tried removing the segue on one button and replace with this action:
#IBAction func strengthButton(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destination = storyboard.instantiateViewController(withIdentifier: "ExerciseViewController") as! ExerciseViewController
destination.componentaccessid = -1;
destination.openedFromUrl = false;
destination.exercise = self.strengthexercise
destination.exercisetype = .strength
self.navigationController?.present(destination, animated: true, completion: nil)
}
But that does exactly the same.
I have a prepare function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier,
let destination = segue.destination as? ExerciseViewController{
destination.componentaccessid = -1;
destination.openedFromUrl = false;
switch identifier{
case "strengthsegue":
destination.exercise = self.strengthexercise
destination.exercisetype = .strength
case "rangesegue":
destination.exercise = self.rangeexercise
destination.exercisetype = .range
case "combinedsegue":
destination.exercise = self.combinedexercise
destination.exercisetype = .combined
default:
print("Nothing")
}
}
}
Try this
self.navigationController?.pushViewController(destination, animated: true)
In your code you are using,
self.navigationController?.present(destination, animated: true, completion: nil)
This will only present the UIViewController.
Instead use,
self.navigationController?.pushViewController(destination, animated: true)
to push the viewController in which the NavigationController is inherited.

Programmatically segue from UIBarButtonItem

I have three view controllers, all with two buttons each on the right and left sides of the navigation bar, as seen below on one of them.
I'm creating these buttons programatically, and instead of writing the code in each respective view controller (VC) I decided to write a Helper class that creates the buttons.
// Note: I am using FontAwesome as a third-party library.
class Helper: NSObject {
static func loadNavBarItems(vc: UIViewController) {
let profileButton = UIBarButtonItem()
let addButton = UIBarButtonItem()
let attributes = [NSFontAttributeName: UIFont.fontAwesome(ofSize: 20)] as [String: Any]
profileButton.setTitleTextAttributes(attributes, for: .normal)
addButton.setTitleTextAttributes(attributes, for: .normal)
profileButton.title = String.fontAwesomeIcon(name: .userCircle)
addButton.title = String.fontAwesomeIcon(name: .plus)
vc.navigationItem.leftBarButtonItem = profileButton
vc.navigationItem.rightBarButtonItem = addButton
}
func segueToProfile(vc: UIViewController) { // I need help here. }
}
I then call Helper.loadNavBarItems(vc: self) from each VC's viewDidLoad().
What I'm trying to do now is to trigger a segue when one of the buttons is pressed (let's assume it's the profile button). So, I need to define profile.action. So, in the Helper class, I have to write a function segueToProfile that takes a view contoller (vc) and runs performSegueWithIdentifier.
The problem is, I'm not fully understanding how to pass in different types of parameters through selectors, and I may be bad at Googling but I cannot find any questions that are close enough to mine for me to understand how to achieve this.
Many thanks in advance for your help.
For reference, this is the structure of my Storyboard.
EDIT: As shown in the storyboard structure screenshot, I've already created a segue from each of the three view controllers to the destination view controller.
To create barButtonItem:
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "😱", style: .plain, target: self, action: #selector(ProfileButtonTapped))
To create action and segue for barButtonItem:
func ProfileButtonTapped() {
print("Button Tapped")
performSegue(withIdentifier: "YourSegueIdentifierName", sender: self)
//If you want pass data while segue you can use prepare segue method
}
Note : To perform segue you have to give segue identifier name from your storyboard.
Output:
Updated:
If you want to connect your destVC without segue you can use below method:
Note: To use below method you have to set storyBoard Id in identity inspector.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let DestVC = storyboard.instantiateViewController(withIdentifier: "DestVcName") as! DestVcName //UINavigationController
self.present(DestVC, animated: true, completion: nil)
i don't know if i understand your problem, but for passing data between viewControllers(embedded in a UINavigationController in your case) using segue, you can do it in:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if(segue.identifier == "yourIdentifier"){
let navPreview : UINavigationController = segue.destination as! UINavigationController
let preview : YourViewController = navPreview.viewControllers[0] as! YourViewController
}}
To add an action to a UIBarButton you should use one of its initializers.
For example
let profileButton = UIBarButtonItem(title: "Your title", style: .done, target: self, action: #selector("Call your function to push View controller"))
Instead of title , you can also set an Image to that button.
You can create barButtonItems with selectors :
let leftBarButton = UIBarButtonItem(image: image, landscapeImagePhone: image, style: UIBarButtonItemStyle.bordered, target: self, action: #selector(didPressLeftButton))
Where the didPressLeftButton is a function :
func didPressLeftButton(){
print("Did press left button")
}

Back button title on custom "Replace" Segue

I have a custom "Replace" segue that replaces the current viewcontroller with another. This works good. However the back button title always becomes "Back". The segue works like this:
ViewController A -> ViewController B
ViewController B then performs a replace segue to ViewController C. The stack is then:
ViewController A -> ViewController C
This is the code for ReplaceSegue.swift:
public class ReplaceSegue: UIStoryboardSegue
{
public var animated = true
public override func perform()
{
let navigationController: UINavigationController = sourceViewController.navigationController!
var controllerStack = navigationController.viewControllers
let index = controllerStack.indexOf(sourceViewController)
controllerStack[index!] = destinationViewController
navigationController.setViewControllers(controllerStack, animated: animated)
}
}
And for my ViewController:
func replaceAgendaViewController()
{
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let agendaViewController = mainStoryboard.instantiateViewControllerWithIdentifier("AgendaViewController") as! AgendaViewController
let segue = ReplaceSegue(identifier: replaceSegueId, source: self, destination: agendaViewController) { () -> Void in
}
segue.animated = false
segue.perform()
}
Have tried this code but I understand why it does not work as it sets the navigationItem on the ViewController that is replaced:
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
I also do not want to set the back button title previous to performing the segue as the ViewController contains other segues that may not want that back button title. Any suggestions?
It was quite easy to fix, I just added the code to the prepareForSegue:sender: in ViewController A:
public override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
switch (segue.identifier!) {
case agendaSegueId:
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
...
}
}

Resources