I have created CustomNavigationController and added some common methods to add UIBarButtonItems to it. Now I want to call those methods from my various viewControllers.
So my question is - How to call methods which belongs to customNavigationController from any other viewController.
I can achieve this in objectiveC by following way :
[(CustomNavigationController *)self.navigationController addLogoutButtonWithVC:self actionLogoutHandler:#selector(actionLogoutHandler)];
where "addLogoutButtonWithVC" is method belongs to CustomNavigationController.
I am trying somewhat in above lines in Swift but no luck.
[Note : I have already replaced NavigationController with CustomNavigationController in storyboard when embedded in, so all navigationControllers are now pointing to CustomNavigationController only]
"Update : Declaration of addLogoutButtonWithViewController and actionLogoutHandler inside CustomNavigationController"
func addLogoutButtonWithViewController(viewCont : UIViewController , selLogout : Selector) {
currentController = viewCont
var barButtonItem : UIBarButtonItem?
barButtonItem = UIBarButtonItem(image: UIImage(named: "Logout.png"), style: UIBarButtonItemStyle.plain, target: self, action: Selector("actionLogoutHandler"))
self.navigationController?.navigationItem.rightBarButtonItem = barButtonItem
}
#objc func actionLogoutHandler() {
print("Inside logout")
currentController?.navigationController?.popToRootViewController(animated: true)
}
Any help in this regard is highly appreciated.
You should try my code as below.
you need object of your navigation controller and use to set in button target.
if let navigationcontroller = self.navigationController as? CustomNavigationController {
navigationcontroller.addLogoutButton(withVC: self,
actionLogoutHandler:#selector(navigationcontroller.actionLogoutHandler(_:))
}
#objc func actionLogoutHandler() {
print("Inside logout")
self.popToRootViewController(animated: true)
}
It should looks like:
if let nav = self.navigationController as? CustomNavigationController {
nav.addLogoutButton(withVC: self,
actionLogoutHandler:#selector(CustomNavigationController .actionLogoutHandler(_:))
}
Try this:
guard let customNavigationController = self.navigationController as? CustomNavigationController else { return }
customNavigationController.addLogoutButtonWithVC()
Related
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")
}
I'm trying to let the user create a new contact. While I've got the screen to prompt the user to put in all his details there is no navigation bar at the top(Like there is in the default Apple Contacts app). There is no way to exit the scene. I'm using the ContactUI framework in swift 2.0 and Xcode 7.3. Here's the code:
// create a new contact
let createNewActionHandler = {(action: UIAlertAction) -> Void in
let newContact = CNMutableContact()
let contactPicker = CNContactViewController(forNewContact: newContact)
contactPicker.delegate = self
contactPicker.navigationController?.setToolbarHidden(false, animated: false)
self.presentViewController(contactPicker, animated: true, completion: nil)
}
Here's what I'm trying to get:
Apple Default
Here's what I have:
What I have
I'm launching the new contact view controller from an action sheet in a tab view controller. I tried embedding the tab in a Navigation view controller but to no effect. I even tried setting the setToolbarHidden property of the navController but it didn't help.
Thanks for any help. I saw the issue raised in other forums but they didn't help.
You have to embed contactViewController to UINavigationController
and Implement Delegate Methods.
let createNewActionHandler = {(action: UIAlertAction) -> Void in
let newContact = CNMutableContact()
let contactPicker = CNContactViewController(forNewContact: newContact)
contactPicker.delegate = self
let navigation = UINavigationController(rootViewController: contactPicker)
self.presentViewController(navigation, animated: true, completion: nil)
}
//MARK: - Delegate
func contactViewController(viewController: CNContactViewController, didCompleteWithContact contact: CNContact?) {
viewController.dismissViewControllerAnimated(true, completion: nil)
}
func contactViewController(viewController: CNContactViewController, shouldPerformDefaultActionForContactProperty property: CNContactProperty) -> Bool {
return true
}
The view controllers must be embedded in UINavigationController and you should push or show view controller:
navigationController?.pushViewController(contactPicker, animated: true)
instead of presenting view controller
Since you are presenting contactPicker viewcontroller on top of current active controller, you will not have access to navigationbar as the view is presented fully,if you want to have button's as in Apple contact app you need to embed your presenting viewcontroller inside UINavigationController, and add left and right bar button items.
Refer the following apple sample which demonstrates the same.
https://developer.apple.com/library/ios/samplecode/iPhoneCoreDataRecipes/Listings/Classes_RecipeAddViewController_m.html
I tried all methods but none worked, So I made a custom method
extension UIBarButtonItem {
private struct AssociatedObject {
static var key = "action_closure_key"
}
var actionClosure: (()->Void)? {
get {
return objc_getAssociatedObject(self, &AssociatedObject.key) as? ()->Void
}
set {
objc_setAssociatedObject(self, &AssociatedObject.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
target = self
action = #selector(didTapButton(sender:))
}
}
#objc func didTapButton(sender: Any) {
actionClosure?()
}
}
extension UIViewController {
func addDissmissButton(){
let cancelButton = UIBarButtonItem.init(title: "Dismiss", style: .plain, target: self, action: nil)
cancelButton.actionClosure = {
self.dismiss(animated: true)
}
self.navigationItem.leftBarButtonItem = cancelButton
}
}
I need your help.
I'm developing a app without Storyboard (using xib), in my project I have two views controllers.
In the main view (after a login) I need to put a button on top of the view to open a left side menu.
This is how I call the MainView on LoginViewController
#IBAction func logar(){
let vc = MainViewController(nibName: "MainViewController", bundle: nil)
self.navigationController!.pushViewController(vc, animated: true)
}
I imported SWRevealViewController, and this is my MainViewController:
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//hide BackButton
self.navigationItem.hidesBackButton = true
//image
var image = UIImage(named: "menuBtn")
image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
//lefBarButonItem
let newBackButton = UIBarButtonItem(image: image, style: .Plain, target: self, action: #selector(MainViewController.openMenu))
self.navigationItem.leftBarButtonItem = newBackButton;
}
func openMenu(){
let frontViewController = LeftMenuTableViewController()
let rearViewController = MainViewController()
//create instance of swRevealVC based on front and rear VC
let swRevealVC = SWRevealViewController(rearViewController: rearViewController, frontViewController: frontViewController);
swRevealVC.toggleAnimationType = SWRevealToggleAnimationType.EaseOut;
swRevealVC.toggleAnimationDuration = 0.30;
//set swRevealVC as rootVC of windows
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window!.rootViewController = swRevealVC
}
}
When I press the button the LeftMenu occupy all the view, not like a partial view.
Any help would be appreciated. Thank you!
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)
...
}
}
I am new to IOS,
I would like to add a UINavigationBar to UITableViewController, I have tried this:
var navBar: UINavigationBar = UINavigationBar(frame: CGRect(x:0, y:0, width:320, height:80))
then,
self.view .addSubview(navBar)
Thanks
You can not simplly add a NavigationBar to UITableViewController like that.
The simplest way to have UINavigationController and NavigationBar is to do it from Storyboard.
Steps:-
Drag the UITableViewController Object from Object Library to the Storyboard.
Highlight the UITableViewController, go to Edit -> Embed In -> Navigation Controller like the screen shot below:-
Go to File -> New -> File.. -> iOS -> Cocoa Touch Class, and create a Custom TableViewController class like below screen shot:-
Finally, go back to storyboard and highlight the UITableViewController object. Under the identity inspector, choose the custom class that you have just created like the screen shot below:-
You may do whatever you want with the custom class file. You may also add a custom UINavigationController class as well if you want and you may attach the custom class to to the object inside the storyboard.
If this is merely the case of showing a modal UITableViewController with a navigation bar, the easiest way to do this from code is to present a UINavigationController with your UITableViewController as the rootViewController:
Presenting view controller:
let sourceSelectorTableViewController = SourceSelectorTableViewController()
let navigationController = UINavigationController(rootViewController: sourceSelectorTableViewController)
self.presentViewController(navigationController, animated: true, completion: nil)
Destination modal UITableViewController:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "cancel")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: "done")
self.title = "Pick a Source"
}
func cancel() {
self.dismissViewControllerAnimated(true, completion: nil)
}
func done() {
//save things
self.dismissViewControllerAnimated(true, completion: nil)
}
Well the best solution will be presenting the
UITableViewController
as
UINavigationController
let topicsList = TopicsListViewController()
let topicsListNavContrl = UINavigationController(rootViewController: topicsList)
presentViewController(topicsListNavContrl, animated: true) { () -> Void in
print("completed")
}
you can do that programmatically .
DataTableViewController *vc = [[DataTableViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];