iOS : bar button doesn't appear - ios

Hi I have two View Controller. there is two segue from first vc to the second vc.I have implemented (with code) right bar button and back button and title in second vc in the view did load .when I segue with button I can see them but when I segue by view controller(for choosing table view cell (did select)) I don't see them
how should I fix them???
this is in the second view did load
override func viewDidLoad() {
super.viewDidLoad()
let img = UIImage(named: "delBtn")
if let top = self.navigationController?.navigationBar.topItem {
top.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: img, style: .plain, target: self, action: #selector(dele))
self.navigationItem.title = "Add/Edit"
pickerS.delegate = self
pickerS.dataSource = self
getMajor()
if preStudent != nil {
receivedData()
}
}
this is segue of first view controller
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let con = controller.fetchedObjects, con.count > 0 {
let obj = con[indexPath.row]
performSegue(withIdentifier: "edit", sender: obj)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "edit" {
if let des = segue.destination as? EditVC {
if let obj = sender as? Student {
des.preStudent = obj
}
}
}
}

Navigation bar is inherited from parent view controller. If your parent view controller is embedded in a navigation controller and you push to a child view controller, your child view controller will have the navigation bar automatically, and thus have the navigation bar items. In your storyboard, check to make sure your segue type is set to show & push. If it's set to present modally, you will lose your navigation bar in your child controller.

Related

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.

How to fix disappearing Tab Bar Controller when changing View Controller initialized from code

I'm struggling with the problem which is when I'm switching view controllers connected with push segue then everything works as expected. The problem is that I have a search bar that executes Table View Controller from code and when I select the cell from that Table View Controller the next view controller is without tab bar.
I am using Table View Controller as a view that displays results from search bar. When I am selecting cell (result from searching) then I am changing view controller showing the results. But this one is done from storyboard.
I know that the Table View Controller executed from code does not "inherits" tab bar from previous controllers through the navigation bar. But this one should be executed from code.
Initialize Search Result Controller
override func viewDidLoad() {
super.viewDidLoad()
self.definesPresentationContext = true
tableView.separatorStyle = .none
self.extendedLayoutIncludesOpaqueBars = true
refreshControlUI = Refresher.configureRefresher()
tableView.refreshControl = refreshControlUI
refreshControlUI.addTarget(self, action: #selector(pullToRefresh), for: .valueChanged)
// Initialize search table
let priceSearchTable = storyboard?.instantiateViewController(withIdentifier: "CoinSearchTable") as! CoinSearchTableViewController
// asignle pricetable as a search results controller
resultSearchController = UISearchController(searchResultsController: priceSearchTable)
resultSearchController?.searchResultsUpdater = priceSearchTable
// Make navigation bar large
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationItem.largeTitleDisplayMode = .never
self.navigationItem.searchController = resultSearchController
// customize search bar
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for coins"
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
}
This code is responsible just for passing values to selected view controller.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if shownAllCoins.count > 0 {
let coinString = shownAllCoins[indexPath.row]
picked = PickedCoin(symbols: [coinString])
picked.delegate = self
navigationController?.setNavigationBarHidden(false, animated: true)
picked.getDetail(name: coinString)
coin = picked.coins[0]
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToDetailsFromSearch" {
if let coin = sender as? [Any]{
if let SearchVC = segue.destination as? DetailPriceViewController{
SearchVC.coin = coin[0] as? Coin
SearchVC.isFromSearching = coin[1] as! Bool
}
}
}
}
Also In Detail View Controller I had to create navigation bar programmatically in case if the segue was from the Search Result Controller.
func addNavBar() {
view.backgroundColor = UIColor(hex: 0x001f3e)
let navBar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 20, width: view.frame.size.width, height: 44))
navBar.barTintColor = UIColor(hex: 0x001f3e)
navBar.isTranslucent = false
self.view.addSubview(navBar);
guard let coin = coin else {return}
let navItem = UINavigationItem(title: "\(coin.symbol)")
let backButton = UIButton(type: .custom)
let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
navBar.titleTextAttributes = textAttributes
saveButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.bookmarks, target: self, action: #selector(self.selectorName(_ :)));
backButton.setTitle("Back", for: .normal)
backButton.setTitleColor(backButton.tintColor, for: .normal) // You can change the TitleColor
backButton.addTarget(self, action: #selector(self.backAction(_:)), for: .touchUpInside)
navItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
navItem.rightBarButtonItem = saveButton
navBar.setItems([navItem], animated: false)
}
Bellow is screenshot of storyboard connection. This storyboard without navigation bar is of course this SearchResultController (Table View Controller that displays results and switches to the detail controller).
What is the best way to set tab bar as a root controller or something. I just need the tab bar to be in all view controller doesn't matter if the controllers are initialize from storyboard or code.
I am trying all day to fix this but I don't know how..
I appreciate every help!
Thanks!
Ok, the problem was with segues. I tried to pass the value from another Controller, that has nothing in common with previous one so that was obvious that navigation bar and tab bar wasn't there. While dealing with separate serchable view controller, there should be added delegation, to pass the vale back to Main Controller. When interacting with searchable view controller, the procedure to pass the value to another view controller in my case looks like this:
When I start typing on my Main Controller the new Searchable View Controller Appear
When I find the item I needed Im selecting it by didSelectedRowAt
I'm Passing the selected value through the delegate function
Added delegate to Main Controller from where should be done segue
Now it works like it supposed to.
The simple code looks like this:
protocol SearchTableDelegate {
func passSelectedValue(selected stock: String)
}
In Searchable View Controller declare:
var delegate: SearchTableDelegate!
In DidSelectedRowAt:
delegate.passSelectedValue(selected: selectedValue)
Now in Main Controller:
class MainTableViewController: UITableViewController, SearchTableDelegate {...}
And use the function from protocol:
func passSelectedValue(selected value: String) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let destinationVC = storyboard.instantiateViewController(withIdentifier: "details") as? DetailsViewController{
destinationVC.value = value
self.navigationController?.pushViewController(destinationVC, animated: true)
}
}
Also when declaring the SearchableViewController in MainController don't forget to assign delegate from Searchable to self:
searchableTableController.delegate = self

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)
...
}
}

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

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 :)
}

Custom UIBarButtonItem in UISplitViewController doesn't respond to clicks (iPhone)

I'm trying to customize the appearance of a UIBarButtonItem on a UISplitViewController divided into two UINavigationController's (the detail and master views), but keep hitting a brick wall. I first followed the advice here, but have been unable to achieve the desired results.
If if I implement the back button in the viewDidLoad method of my Detail View Controller in this way:
if let svc = splitViewController {
navigationItem.leftBarButtonItem = svc.displayModeButtonItem()
}
I get the default 'Back' button that navigates back to Master View controller. But if I try to customize the button using the following code:
if let svc = splitViewController {
let searchButton = UIBarButtonItem(
image: UIImage(named: "magnifying-glass"),
style: UIBarButtonItemStyle.Plain,
target: svc.displayModeButtonItem().target,
action: svc.displayModeButtonItem().action
)
navigationItem.leftBarButtonItem = searchButton
}
I get the desired icon in the navbar but it does nothing when clicked. I've debugged the action and target in the console and for both the action is "_triggerDisplayModeAction:" and the controller is an instance of the UISplitViewController.
Any ideas on what's going on here?
i suggest you to create and use your custom UIBarButtonItem in prepareForSeque in master view controller. split view controller by default will create new instance of your detail view controller!!! ignore the part with iPhone ios7support, that is another story :-) https://github.com/op183/MasterDetailDemo
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = objects[indexPath.row] as String
if let navController = (segue.destinationViewController as? UINavigationController) {
let controller = navController.topViewController as DetailViewController
controller.detailItem = object
let defaultBarButton = splitViewController?.displayModeButtonItem()
let searchButton = UIBarButtonItem(
image: UIImage(named: "magnifying-glass"),
style: UIBarButtonItemStyle.Plain,
target: defaultBarButton.target,
action: defaultBarButton.action
)
controller.navigationItem.leftBarButtonItem = searchButton
controller.navigationItem.leftItemsSupplementBackButton = true
} else {
// iPhone ios7support
(segue.destinationViewController as DetailViewController).detailItem = object
}
}
}
}

Resources