I would like to use the solution provided by Travis M. to this question:
How to use dismiss an iPhone popover in an Adaptive Storyboard
However, I would need the below be translated to objective c.
Anyone could help out?
If what you want is a popover on your iPad but a modal sheet with a close button on your iPhone then you can do it without creating an extra navigation controller in storyboard for the popover.
In Xcode 6.3 storyboard, you simply hook up a view controller and designate the segue as a "Present as Popover"
The code below should go in the view controller that segues to the popover, not in the popover itself:
First you set up the popover delegate:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "myPopoverSegueName") {
let vc = segue.destinationViewController
vc.popoverPresentationController?.delegate = self
return
}
}
Then you add the delegate extension (below your view controller's code) and create the navigation controller / close button on the fly:
extension myViewController: UIPopoverPresentationControllerDelegate {
func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
let btnDone = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss")
let nav = UINavigationController(rootViewController: controller.presentedViewController)
nav.topViewController.navigationItem.leftBarButtonItem = btnDone
return nav
}
}
Then you add the delegate extension (below your view controller's code) and create the navigation controller / close button on the fly:
extension myViewController: UIPopoverPresentationControllerDelegate {
func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
let btnDone = UIBarButtonItem(title: "Done", style: .Done, target: self, action: "dismiss")
let nav = UINavigationController(rootViewController: controller.presentedViewController)
nav.topViewController.navigationItem.leftBarButtonItem = btnDone
return nav
}
}
Then you add your dismiss function and you should be good to go:
func dismiss() {
self.dismissViewControllerAnimated(true, completion: nil)
}
It's not that different from objective-c. You could probably figure out what's going on just by looking at it. It's just setting up prepare for segue and implementing part of a protocol.
myViewController needs to declare itself as conforming to UIAdaptivePresentationControllerDelegate. Then this is the code.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"myPopoverSegueName"]) {
UIViewController *viewController = segue.destinationViewController;
viewController.popoverPresentationController.delegate = self
}
}
- (UIViewController *)presentationController:(UIPresentationController *)controller
viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
{
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(dismiss)];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller.presentedViewController];
navController.topViewController.navigationItem.leftBarButtonItem = doneButton;
}
-(void) dismiss
{
[self dismissViewControllerAnimated:YES completion:nil];
}
You don't need to use an extension or category. Not going to vouch for whether this does what Travis M. says it does.
Related
I want to be able to change the action of the back bar button item on a specific UIViewController in my navigation controller so that it pops to the root view controller. I've tried the following but they don't work:
let backButton = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(back))
self.navigationItem.backBarButtonItem = backButton
and
self.navigationItem.backBarButtonItem?.action = #selector(back)
Any suggestions?
You should use self.navigationItem.leftBarButtonItem = backButton
Good luck
First of all backBarButtonItem action not works because you can only change back button title,take a look question about it here.
Solution
In ViewController from which you want to pop to root ViewController you need to set as a delegate of UINavigationControllerDelegate
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
and implement UINavigationControllerDelegate this method`
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if viewController.isKind(of:PreviousViewController.self) {
navigationController.popToRootViewController(animated: animated)
}
}
If my answer not fit your needs you can check similar question here.
To keep the same look and feel of the back button but change the action, see the ViewWillDisappear answer to the question regarding, "Execute action when back bar button of UINavigationController is pressed" Execute action when back bar button of UINavigationController is pressed
This is your solution, just need to set target and selector, nothing more.
private func setNavBar() {
let item = navigationItem.backBarButtonItem
item?.target = self
item?.action = #selector(self.donePressed)
navigationItem.leftBarButtonItem = item
}
#objc private func donePressed() {
self.dismiss(animated: true, completion: nil)
}
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'm trying to implement the new UIPopoverPresentationController in my iPhone app (using Objective C). What I want is a simple popover with a tableview that emanates from the initiating button.
--Edit--
Here's my REVISED code, adapted from research in the docs, SO, and from input in comments below:
- (IBAction)selectCategoryBtn:(UIButton *)sender
{
[self performSegueWithIdentifier:#"CatSelectSegue" sender:self.selCatButton];
}
-(void) prepareForSegue:(UIStoryboardSegue *) segue Sender:(id) sender
{
if (sender == self.selCatButton)
{
if ([segue.identifier isEqualToString:#"CatSelectSegue"])
{
UIPopoverPresentationController *controller = segue.destinationViewController;
controller.delegate = self;
controller.sourceView = self.selCatButton;
controller.sourceRect = self.selCatButton.frame;
}
}
}
-(UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
return UIModalPresentationNone;
Here's my storyboard hookup:
However this simply presents a tableview in a modal fashion, rising up from the bottom and consuming the entire screen.
I've googled, and looked all over SO, but it appears that I'm not the only one confused by what I'd hoped would resolve a nettlesome issue for the iPhone.
Can anyone see a glitch in my code or direct me to a clear tutorial? I've looked, but maybe the API is just so new nobody's got a handle on it yet.
Thanks!
2nd edit:
Here's what gets presented as a result of the code above. I reduced the size of the tableview in the View Controller I expected to be presented as a popover. I colored the background gray, just to clarify what's showing up instead of the popover.
Steps:
A) Link your UIButton to the popover's view controller using the Present As Popover segue type. I actually had to create a new project to get this to appear but it's probably something to do with the base SDK.
B) Make the View Controller containing the UIButton conform to the <UIPopoverPresentationControllerDelegate>. E.g. In your MyViewController.m file add:
#interface MyViewController () <UIPopoverPresentationControllerDelegate>
C) Add the method below to the View Controller containing the UIButton:
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
}
D) Add the following into your prepareForSegue:sender: replacing your segue.identifier check:
if ([segue.identifier isEqualToString:#"CatSelectSegue"]) {
UIViewController *dvc = segue.destinationViewController;
UIPopoverPresentationController *controller = dvc.popoverPresentationController;
if (controller) {
controller.delegate = self;
}
}
Code tested and proof it works:
Edit: My test app TPOPViewController.m file where the magic happens:
#import "TPOPViewController.h"
#interface TPOPViewController () <UIPopoverPresentationControllerDelegate>//, UIAdaptivePresentationControllerDelegate>
#end
#implementation TPOPViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifier = segue.identifier;
if ([identifier isEqualToString:#"popover"]) {
UIViewController *dvc = segue.destinationViewController;
UIPopoverPresentationController *ppc = dvc.popoverPresentationController;
if (ppc) {
if ([sender isKindOfClass:[UIButton class]]) { // Assumes the popover is being triggered by a UIButton
ppc.sourceView = (UIButton *)sender;
ppc.sourceRect = [(UIButton *)sender bounds];
}
ppc.delegate = self;
}
}
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone;
}
#end
My test storyboard as well:
Apparently the above method no longer works with iOS9/Xcode7. This is because if you set the segue style to "Popover" using Interface Builder, Xcode ignores it when it compiles your application. Furthermore, it automatically sets the segue back to "Push" the next time you open your project. If you have version control software like Git, you'll be able to observe this unwanted change being made.
However, it's still possible to get iPad-style popovers on the iPhone if you manually present the view controller that you want to show as a popover. Example Swift code:
// ViewController.swift
// PopoverDemo
//
// Created by bhnascar on 12/2/15.
// Copyright © 2015 bhnascar. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
/* The bar button item that will present the popover. */
var popoverButton: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
popoverButton = UIBarButtonItem(title: "Pop!", style: UIBarButtonItemStyle.Plain, target: self, action: "presentPopover")
self.navigationItem.rightBarButtonItem = popoverButton
}
// Mark: - UIPopoverPresentationControllerDelegate
func prepareForPopoverPresentation(popoverPresentationController: UIPopoverPresentationController) {
popoverPresentationController.permittedArrowDirections = .any
popoverPresentationController.barButtonItem = popoverButton
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
// Mark: - Callback function for popover button.
func presentPopover() {
let popoverContentController = UIViewController()
popoverContentController.view.backgroundColor = .blue
// Set your popover size.
popoverContentController.preferredContentSize = CGSize(width: 300, height: 300)
// Set the presentation style to modal so that the above methods get called.
popoverContentController.modalPresentationStyle = .popover
// Set the popover presentation controller delegate so that the above methods get called.
popoverContentController.popoverPresentationController!.delegate = self
// Present the popover.
self.present(popoverContentController, animated: true, completion: nil)
}
}
SWIFT 3.X
This will show the popover at the center of the screen
class CommonViewController: UIViewController, UIPopoverPresentationControllerDelegate{
func adaptivePresentationStyle(
for controller: UIPresentationController,
traitCollection: UITraitCollection)
-> UIModalPresentationStyle {
return .none
}
func showPopover() {
let myViewController = UIViewController()
myViewController.preferredContentSize = CGSize(width: 320, height: 200)
myViewController.modalPresentationStyle = .popover
let popOver = myViewController.popoverPresentationController
popOver?.delegate = self
self.present(myViewController, animated: true, completion: nil)
popOver?.permittedArrowDirections = .up
popOver?.sourceView = self.view
let rect = CGRect(
origin: CGPoint(x: self.view.frame.width/2,
y: self.view.frame.height/2),
size: CGSize(width: 1, height: 1)
)
popOver?.sourceRect = rect
}
}
To present UIModalPresentationStyle popover from iPhone/iPad:
-(void)menuButtonPressed:(UIButton *)sender {
self.menuPopoverController = [[DownloadMenuPopoverController alloc] initWithStyle:UITableViewStylePlain];
self.menuPopoverController.delegate = self;
self.menuPopoverController.modalPresentationStyle = UIModalPresentationPopover;
self.menuPopoverController.popoverPresentationController.delegate = self;
self.menuPopoverController.preferredContentSize = CGSizeMake(250,80);
self.menuPopoverController.popoverPresentationController.sourceRect = sender.frame;// rect to show view
self.menuPopoverController.popoverPresentationController.sourceView = self.view;
UIPopoverPresentationController *popPC = self.menuPopoverController.popoverPresentationController;
popPC.permittedArrowDirections = UIPopoverArrowDirectionAny;
popPC.delegate = self;
[self presentViewController:self.menuPopoverController animated:YES completion:nil];
}
#pragma mark - UIPresentationController Delegate methods
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection {
return UIModalPresentationNone;
}
- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style {
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller.presentedViewController];
return navController;
}
**Made a barbutton item like this** -
let dotImage = UIImage(named: "dot")?.withRenderingMode(.alwaysOriginal) let searchImage = UIImage(named: "search")?.withRenderingMode(.alwaysOriginal) // let shareImageButton = UIBarButtonItem(image: shareImage, style: .plain, target: self, action: #selector(didTapshareImageButton(sender:))) let dotImageButton = UIBarButtonItem(image: dotImage, style: .plain, target: self, action: #selector(didTapdotImageButton(sender:))) let searchButton = UIBarButtonItem(image: searchImage, style: .plain, target: self, action: #selector(didTapSearchButton(sender:)))
**Added popover on this -**
#objc func didTapdotImageButton(sender: UIBarButtonItem){
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ShareVC")
vc.modalPresentationStyle = .popover
let popover: UIPopoverPresentationController = vc.popoverPresentationController!
popover.barButtonItem = sender
popover.delegate = self
sender.image = UIImage(named: "close")?.withRenderingMode(.alwaysOriginal)
present(vc, animated: true, completion: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];
After going through every single stackoverflow solution for this problem, it's still frustratingly not working for me.
//UIBarButtonItem declaration
UIBarButtonItem* button1 = [[UIBarButtonItem alloc] initWithTitle:#"Button Text"
style:UIBarButtonItemStyleBordered target:self action:#selector(myAction)];
//method 1
[self setToolbarItems:[NSArray arrayWithObjects: button1, nil] animated:YES];
//method 2
[self.navigationController.toolbar setItems:[NSArray arrayWithObject:button1]];
//method 3
self.navigationController.toolbarItems = [NSArray arrayWithObject:button1];
//displaying toolbar
[self.navigationController setToolbarHidden:NO];
None of the above methods work for displaying a button on the toolbar - all I get is a blank toolbar. Is there something obvious I'm missing here?
Move
//UIBarButtonItem declaration
UIBarButtonItem* button1 = [[UIBarButtonItem alloc] initWithTitle:#"Button Text"
style:UIBarButtonItemStyleBordered target:self action:#selector(myAction)];
//method 1
[self setToolbarItems:[NSArray arrayWithObjects: button1, nil] animated:YES];
//displaying toolbar
[self.navigationController setToolbarHidden:NO];
to viewDidAppear:(BOOL)animated this is the point where UINavigationController get toolbar items of UIViewController that it manages.
use
self.toolbarItems=[NSArray arrayWithObject:button1]
With Swift 3 / iOS 10, in the simplest case where your navigation controller will contain only one view controller, you may use the code below in order to display your view controller with a toolbar that contains a bar button item:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Show navigation controller’s built-in toolbar
navigationController?.setToolbarHidden(false, animated: false)
// Set the view controller toolbar items
let items = [UIBarButtonItem(title: "Button Text", style: .plain, target: nil, action: nil)]
setToolbarItems(items, animated: false)
}
}
However, if you plan to have several view controllers in your navigation controller's stack, you will have to call UINavigationController's setToolbarHidden(_:animated:) method in viewWillAppear() and viewWillDisappear() in order to properly show or hide the navigation controller’s built-in toolbar:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set the view controller toolbar items
let items = [UIBarButtonItem(title: "Button Text", style: .plain, target: nil, action: nil)]
setToolbarItems(items, animated: false)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Show navigation controller’s built-in toolbar
navigationController?.setToolbarHidden(false, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Hide navigation controller’s built-in toolbar
navigationController?.setToolbarHidden(true, animated: false)
}
}
For those looking for a Swift version, try this:
let someVC: UIViewController = ...
let someButton: UIBarButtonItem = ...
someVC.setToolbarItems([someButton], animated: true)
The UINavigationController.toolbar property documentation explicitly clarifies which API should be used for setting toolbar items:
Management of this toolbar’s contents is done through the custom view controllers associated with this navigation controller. For each view controller on the navigation stack, you can assign a custom set of toolbar items using the setToolbarItems:animated: method of UIViewController.
-- UINavigationController Class Reference