I have an app that displays a webView of a web page. The web consists of a main page with a lot of subpages. When I am on the main page I want the navigation Back button to to keep the default behaviour of going back to the previous view controller. But when the web is on a subpage I would like the navigation Back button to take the web to the main web page.
So I need to catch when the user presses the back button and if he is on a web subpage take him to the web main page and prevent the webView from closing. I was thinking that I can catch the back button using viewWillDisappear(), but how can I prevent the view from closing?
Similar to KickimusButticus' answer:
If you want to more easily do it using the storyboard and such, you could hook up your back button to an IBAction like so:
#IBAction func back() {
}
And have internal testing for whether or not the user is in a sub-page of the webview. If they are, you can mess with the webview. If they are on the home page, you could simply use the back button to segue to your main view controller or use the self.dismissViewControllerAnimated(true, completion: nil) to dismiss that view controller.
Check this post out:
Execute action when back bar button of UINavigationController is pressed
Something like this ought to do, in your view controller code:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true
let customBackButtonItem = UIBarButtonItem(title: "Back", style: .Bordered, target: self, action:#selector(customBackMethod(_:)))
self.navigationItem.leftBarButtonItem = customBackButtonItem
}
// #objc is so we can use #selector() up above
#objc func customBackMethod(sender: UIBarButtonItem) {
if webView.canGoBack {
webView.goBack()
}
}
Note that you may have to use UIBarButtonItem(image:, style:, target:, action:) if you want a 'back' image, or UIBarButtonItem(customView:). See this question and answer for more details: https://stackoverflow.com/a/36180934/892990
Related
I'am new to ios. I have several view controllers and I want back button to get user to level up controller. Example.
But if user comes from gameOver view, back button sent him back to gameOver and I don't want such behavior (I want user to be sent at second level (games level) controller as shown). On android I could set the pop behavior for the navigation actions with mouse very easily.
What is the correct way to do the same in ios? Or I have to create the custom back button and do everything manually?
Using Swift this can be achieved using below code:
func popToViewController(_ specificViewController: Swift.AnyClass) {
let viewControllers = self.navigationController!.viewControllers
for eachViewController in viewControllers {
if eachViewController.isKind(of: specificViewController) {
self.navigationController!.popToViewController(eachViewController, animated: true)
break;
}
}
}
Usage:
self.popToViewController(gameLevelViewController.self)
I've a table view with navigation controller embedded in. I've added a UIBarButtonItem (add) button. When I click this button it opens a new view where user enters the data and submits it (which makes a web service call) and returns back to the previous view. This navigation happens as shown below,
func addTapped(_ sender:UIBarButtonItem) {
print("Called Add")
let vc = (storyboard?.instantiateViewController( withIdentifier: "newNote")) as! newNoteVC
self.navigationController?.pushViewController(vc, animated: true)
}
And in new view I do following,
#IBAction func saveButton(_ sender: UIButton) {
if (self.noteDescription.text?.isEmpty)! {
print("Enter missing note description")
return
} else {
let desc = self.noteDescription.text
self.uploadNote(noteText: desc!, noteDate: self.dateInMilliseconds)
self.navigationController?.popViewController(animated: true)
}
}
This way a record gets saved and a view gets popped from the navigation controller stack but only thing I don't how to do is refresh the table view data in the parent view (where I might need to make a new http service call to read all the records).
I hope I'm able to explain the issue? Any help is appreciated.
As mentioned in the comments, making a service call just to update the tableview might be a overkill. However, if this is the business scenario which needs to be implemented, you can do the same in
func viewWillAppear
in the parent view controller. You can make the service call in this method and reload the table view with the data.
You would also need to check the overall navigation of the application as making service calls in viewWillAppear method is not a good approach as these gets called everytime the view is shown. For Ex: If coming back from a navigated view then also the method is called.
I'm trying to change UIBarButtonItem text "cleanly" during runtime so that Edit/Done modes can be toggled. Every time I change the title attribute during runtime, however, the animation seems clumsy. I'm looking to emulate the appearance and function of the Edit/Done button in the Contacts app (where Edit simply fades and Done appears in its place).
#IBAction func editDoneButtonPressed(sender: UIBarButtonItem) {
if(sender.title == "Edit"){
sender.title = "Done"
}else{
sender.title = "Edit"
}
}
Initial settings: Identifier is set to "custom" and Title is set to "Edit"
Simpler programatical solutions are preferred, however, the appearance of the animation is indeed paramount. I'd consider toggling the UIBarButtonItem identifier, rather than its text attribute, however, I'm not sure of an elegant way to toggle the identifier during runtime.
BTW: I'm using Swift to construct the app.
Links to...
Screencast of Contacts app Edit/Done toggle animation:
https://youtu.be/5mT_vzpNhIw
Screencast of my Edit/Done toggle animation:
https://youtu.be/mEweotJNVNE
Thank you.
There's a much better way, a standard way, to get an Edit/Done button.
UIViewController provides a standard Edit/Done button that is already hooked into the editing property of the view controller.
A common usage is as follows:
Inside the viewDidLoad method of your view controller, use the standard button:
self.navigationItem.rightBarButtonItem = editButtonItem
Put the editButtonItem wherever you actually need it.
Then, instead of setting up your own action, simply override the setEditing(_:animated:) method:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if (editing) {
// user just tapped the Edit button (it now says Done)
} else {
// user just tapped the Done button (it now says Edit)
}
}
I am creating an app, and to access the main screen of the app the user has to input there fingerprint. I have it setup so that when the fingerprint is correct it programmatically performs a segue to a navigation controller which is connected to the main view controller. Here is my code:
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Authenticate with Touch ID"
context.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reason, reply:
{(succes: Bool, error: NSError!) in
if succes {
self.showOrHide(true)
ProgressHUD.showSuccess("Success")
self.performSegueWithIdentifier("passwordCorrectSegue", sender: nil)
} else {
}
})
} else {
self.touchIDLabel.hidden = true
self.touchIDImage.hidden = true
}
The problem is when I perform the segue and it goes to the navigation controller which shows the view controller, the UIBarButtonItem's do not show on the top left and top right of the screen. You can still click on the top left and top right of the screen and the actions for those buttons would run. The problem is that the UIBarButtonItem's are just not showing. Another thing I have tried is that you also have the option to enter in a password, and when the password is correct it goes to the next view controller... and it works perfectly. Does anyone know how to fix this?
The UIBarButtonItems just don't show when I use the method
self.performSegueWithIdentifier("passwordCorrectSegue", sender: nil)
when trying to perform that method using the fingerprint method.
I had the exact same issue: I have 2 VC, each being able to segue to a third one ; if I segue from the first, the right bar button item is not visible (but still works), but if I segue from the second one, the right bar button item is visible.
I guess it's a bug in iOS9.
The workaround I used was to force the initialization of the right bar button item in the destination view controller "viewDidLoad":
override func viewDidLoad() {
super.viewDidLoad()
// Force right bar button item when using performSeguewithIdentifier (bug in iOS9?)
navigationItem.rightBarButtonItem = UIBarButtonItem(
image: UIImage(assetIdentifier: .Profile),
style: UIBarButtonItemStyle.Plain,
target: self,
action: Selector("displayUser"))
}
That fixed the issue for me (at least until Apple fixes this bug).
I have a settings screen, in that I have a table cell. By clicking on that I take to another screen where user can choose an option and I want it back in the previous view controller when back button is pressed.
I can put a custom bar button item, but I want to return to the parent view controller using the back button in the navigation bar rather than with a custom button on the view.
I don't seem to be able to override the navigation back button to point it down to my unwind segue action and since the back button doesn't appear on the storyboard, I cant drag the green Exit button to it
Is it possible to unwind a push segue with the back button?
Here's my solution, based on Objective-C code from Blankarsch to this StackOverflow question: How to trap the back button event
Put this code inside the View Controller you want to trap the Back button call from:
override func didMoveToParentViewController(parent: UIViewController?) {
if (!(parent?.isEqual(self.parentViewController) ?? false)) {
println("Back Button Pressed!")
}
}
Inside of the if block, handle whatever you need to pass back. You'll also need to have a reference back to calling view controller as at this point most likely both parent and self.parentViewController are nil, so you can't navigate the View Controller tree.
Also, you might be able to get away with simply checking parent for nil as I haven't found a case where pressing the back button didn't result in parent being nil. So something like this is a bit more concise:
override func didMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
println("Back Button Pressed!")
}
}
But I can't guarantee that will work every time.
Do the following in the view controller that has the back button
Swift 3
override func didMove(toParentViewController parent: UIViewController?) {
if !(parent?.isEqual(self.parent) ?? false) {
print("Parent view loaded")
}
super.didMove(toParentViewController: parent)
}
I tried the same and it seems that you cannot catch the standard back button's action so the solution will be to use a custom button and bind it to a segue which leads back to the previous page.
You could use some sort of delegation as you did or use a custom back button and an unwind segue.
Better even, you could handle passing data between your view controllers using a mediator:
http://cocoapatterns.com/passing-data-between-view-controllers/