Edit: I forgot to mention that I can't use Apple's predefined edit button because I need the button in the toolbar not the navigation bar (I couldn't figure out a way to add Apple's edit button on the toolbar. If this is possible then please let me know).
I know this question seems like a lot of other questions that have been asked but I have tried the solutions from all of those questions and non of them have helped me solve this problem.
I've researched this problem and tried every solution that I found and could think of.
I've got a UIBarButtonItem which is in my toolbar. It is working as an edit button, so when clicked I want the title to change back to Done. However, whenever I change the title nothing happens. I've tested this value as well and when I print out the value for title I get nil every time. I can't figure this out...
#IBOutlet weak var customEditButton: UIBarButtonItem!
func viewDidLoad() {
super.viewDidLoad()
tableView.setEditing(false, animated: false)
customEditButton.title? = "Edit"
print("customEditButton = \(customEditButton.title)") //This prints nil
...
}
#IBAction func editButtonIsClicked(_ sender: UIBarButtonItem) {
print("MarksTable: editButtonIsClicked")
if (self.tableView.isEditing) {
tableView.setEditing(false, animated: true)
customEditButton.title? = "Edit"
}
else {
tableView.setEditing(true, animated: true)
customEditButton.title? = "Done"
}
}
This not the only thing I've tried. I have also tried
customEditButton = UIBarButtonItem.init(title: "Done", ...)
but this also failed.
Does anyone know why this is happening or have a solution to the problem?
So I was able to reproduce your issue on my own project and I have it running correctly.
It is possible that your issue lies in the System Item type of the button you have added to the Toolbar. Go to storyboards, select your button, and make sure you set its System Item to Custom in the Attributes Inspector, instead of Edit. Initially, I tried using Edit but its just not as simple to change the title as if you have it set as Custom.
After this I created an outlet and an action from the button, just as you did in your code.
Here's the outlet ->
#IBOutlet weak var editBarButton: UIBarButtonItem!
And here's the button action:
#IBAction func tappedEditButton(sender: UIBarButtonItem) {
if tableView.editing {
tableView.setEditing(false, animated: true)
editBarButton.title = "Edit"
} else {
tableView.setEditing(true, animated: true)
editBarButton.title = "Done"
}
Hopefully this works well to solve your problem, best of luck!
Related
I am trying to have a switch appear in the off state once a view loads. Not always, but only if a boolean value that I created("switchBool") is false.
I've tried using the two ways on the apple documentation website. The two ways are shown in my code example. One is commented out.
import UIKit
class ViewController: UIViewController {
var switchBool = false
#IBAction func switchControl(_ sender: UISwitch) {
//sender.isOn = switchBool
sender.setOn(switchBool, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
The app is building and running without errors. However, I want the switch to be in the off state if "false" is assigned to the bool "switchBool", which in my example it is, but no matter what I try the switch always appears in the on state when the view loads up.
You need to create an IBOutlet for your switch(using the assistant editor button and storyboard). Then, you can just set the switch to be off in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
yourSwitch.setOn(switchBool, animated: true)
}
In the storyboard you can set it to be off in the Attribute inspector
I'm testing an app and I discovered that when double tapping the back button, it automatically pops to root view controller of the navigation controller. There is no code anywhere that does this explicitly and I was wondering if this is the default behavior of the back button in a navigation controller.
I've tested the same thing in another app and the issue wasn't there, so the only thing that explains this is that something else is triggering that popToRoot and I can't figure out what.
Sorry, but I cannot post the code here. Just want your opinion on this.
Thanks in advance!
As I commented above at Glenns answer, dispatching the popViewController into the main thread might be a problem because it will be executed also when a double tap is recognized. Maybe this is a slightly more "stable" solution:
private func setupBackButton() {
let myBackButton = UIButton(type: .custom) // or whatever
// setup title, image etc.
let myCustomBackButtonItem = UIBarButtonItem(customView: myBackButton)
self.navigationItem.leftBarButtonItem = myCustomBackButtonItem
// Create gesture recognizers for single and double tap:
let singleRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.singleTap))
singleRecognizer.numberOfTapsRequired = 1
let doubleRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.doubleTap))
doubleRecognizer.numberOfTapsRequired = 2
// single tap should only fire if double tap is not recognized
singleRecognizer.require(toFail: doubleRecognizer)
myBackButton.addGestureRecognizer(singleRecognizer)
myBackButton.addGestureRecognizer(doubleRecognizer)
}
#objc private func singleTap() {
self.navigationController?.popViewController(animated: true)
}
#objc private func doubleTap() {
self.navigationController?.popToRootViewController(animated: true)
}
When I saw this question, I implemented the idea onto my current project, and it worked, thanks for this. So an idea to do this is have a custom back button. I could post the whole code to this but that would make it a spoonfeeding. Anyways, here goes the steps:
Make a UIButton and make it your custom back button UIBarButtonItem in your navigationController's navigationBar if you ever use one.
The selector would be obviously like this, don't mind much the code inside, cause this is taken out of my project, but you get the idea:
func back() {
if let navCon = self.navigationController {
navCon.popViewController(animated: true)
}
}
And for the double tap, you'll need a tapGesture for that. I have this one declared like so:
private lazy var doubleTapGesture: UITapGestureRecognizer = {
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.doubleTap))
gesture.numberOfTapsRequired = 2
return gesture
}()
and add that to your button. Selector would be like this:
#objc private func doubleTap() {
self.navigationController?.popToRootViewController(animated: true)
}
VOILA! Works smoothly. Easy, right? I hope this helps!
Thank you for all your answers but my issue was that I DIDN'T want that behavior, my post was misinterpreted and I'm sorry for that.
Otherwise, I solved myself the problem. There was a subview added to navigation controller's navigation bar which was hidden up, below the bar. And somehow, that subView had interaction enabled even if it was below. So I disabled interaction when hidden. That's all.
short: I don´t understand how to programmatically add the default back button.
long: I have been asked to write an iOS app, without any previous experience I decided
to follow the advices and code given by Nicola Zaghini.
In the code given along with the article, I really don´t understand
where does the back button come from.
The app has three screens
one to choose a city (folder WeatherLocation)
one that displays all
cities that have been already choosen (folder WeatherList)
one that
displays the weather for a city clicked in the list (folder
WeatherDetail)
There is + button for WeatherLocation:
This button is added in the code but I can not find where
and how is coded the back button in WeatherDetails (see above), and how is coded the action to do when one click the back button.
I search the web and found how to set a button in the NavigationBar:
let leftBarButtonItem: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem(title: "Left Item", style: .plain, target: self, action: nil)
barButtonItem.tintColor = UIColor.red
return barButtonItem
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.leftBarButtonItem = leftBarButtonItem
}
I also found that there is a backBarButtonItem
but I could not find how to properly use this property.
Moreover in the code of Nicola Zaghini there is nothing like backBarButtonItem to enable the
back button, neither in the xib and storyboard.
Can someone give me some hints about on how to set the back button ?
Thanks a lot!!
In the code of Nicola Zaghini the secret to have a default back button resides (for example) in the function navigateToWeatherDetail in the class WeatherListDefaultRouter where the new ViewController is pushed on a NavigationController:
func navigateToWeatherDetail(withLocation location: Location) {
if let weatherDetailVC = self.weatherDetailBuilder()?.buildWeatherDetailModule(withLocation: location) {
self.viewController?.navigationController?.pushViewController(weatherDetailVC, animated: true)
}
}
There are few similar kind of questions asked and answered by many on Stackoverflow. But none of solution matches with my requirement.
I am using Swift3 for IOS mobile app development and used Navigation controller to manage the navigation. I gave title to all pages using below code.
self.title = "Title"
When I move to next page, then it shows me back button with the earlier page title. For some pages, title is long and it disturbs my header section of page.
Instead of page title, I want to change button text to "Back".
Any idea how to do that?
if you are using storyboard then you can set back button title there, click on ViewController -> Navigation Item -> Back Button and set "Back" title.
alternatively you can set title in ViewWillApear and change it to "Back" in viewWillDisappear method
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.title = "My Title"
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.title = "Back"
}
In your prepare function before the segue do this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let backItem = UIBarButtonItem()
backItem.title = "Back" // Change to desired title here
navigationItem.backBarButtonItem = backItem
}
Try to remove the title on viewWillDisapperar then re enter it on viewWillAppear
Try this:
self.navigationController?.navigationBar.topItem?.title = "Back"
It should work :)
Try this :
self.navigationController!.navigationBar.topItem!.title = "Back"
Title essentially says it all.
I have a UITableView and I want the RightBarButtonItem item to disappear while the UITableView is in edit mode. Unfortunately, all of the answers that I have found so far suggest setting the button to nil... which won't work for me because I do not want to get rid of the button and the reference to it, just hide it while the UITableView is in edit mode.
What I'm having trouble figuring out what to do, then, is:
Detect when the UITableView has entered editing mode
Hiding the RightBarButtonItem (not removing it entirely)
Detect when the UITableView has left editing mode (so the button can reappear)
Any help would be appreciated, thank you!
My working solution for anyone who needs it:
override func setEditing(editing: Bool, animated: Bool) {
if (editing) {
super.setEditing(true, animated: true)
self.navigationItem.rightBarButtonItem!.enabled = false
} else {
super.setEditing(false, animated: true)
self.navigationItem.rightBarButtonItem!.enabled = true
}
}
Make sure to set the super.setEditing before and after editing starts in order to preserve the functionality of the edit button.
In addition, if you don't want the UITableView to remain in edit mode when the user leaves the UITableView and doesn't click "done" first, add the following function:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
if (editing) {
editing = false
}
}
You can use an optional datasource method to detect when a row is being edited, tableView(_:canEditRowAtIndexPath:)
And inside that method, you can either hide or disable the bar button item (disabling is probably the friendlier thing to do, in terms of the UI and code). There is no hidden property on a bar button, so to properly hide it means you potentially do some grody coding to temporarily remove or make it disappear.
Anyways, I suggest something like:
func tableView(tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
self.navigationItem.rightBarButtonItem.enabled = false
return true
}
I had same challenge with two different bar buttons which I needed to hide and show on some conditions. First was "edit" button to enter editing mode on my view and second was "done" to save changes. So they had to appear interchangeably.
I made an extenstion to UINavigationItem class where implemented two methods. One to delete item from bar. Second to add item to bar. As a parameter to these methods I passed IBOutlets of the items which I made strong so they would not deallocate when item is removed from bar
extension UINavigationItem {
func deleteFromRightBar(item: UIBarButtonItem) {
// make sure item is present
guard let itemIndex = self.rightBarButtonItems?.index(of: item) else {
return
}
// remove item
self.rightBarButtonItems?.remove(at: itemIndex)
}
func addToRightBar(item: UIBarButtonItem) {
// make sure item is not present
guard self.rightBarButtonItems?.index(of: item) == nil else {
return
}
// add item
self.rightBarButtonItems?.append(item)
}
}
I used these methods like that
navigationItem.deleteFromRightBar(item: doneButton)
navigationItem.addToRightBar(item: editButton)