Pop up a View from a Button containing information but keeping the current View behind it: Swift implementation - ios

I am finishing my career project and I want to make some UI improvements on the application I developed in Swift for iOS.
The application does not have any explicit explanation of how it works and I'd like to detail it in a pop-up View called from an information button in the Navigation Bar. I'd like some kind of View similar to the Notification Alerts, which pop-up in front of the current view and they disappear by pressing another button. I give you an example image:
I want this button to be available from any Tab View (I've got three views). Any idea of how to manage that?
Thank you very much!

First, go to IB and create a new View Controller. In the Attributes Inspector, click on the dropdown next to Size and click Freeform. Set the size to what you want. Then click Use Preferred Explicit size.
Control-drag from your info button to the new VC you created. Click on the segue, then go to the attributes inspector and change the segue type to 'Present As Popover.' Change the segue identifier to whatever you want. I named it popover.
Create a new class for your popover view and assign it to the VC you created. It doesn't need any code at the moment.
In your presenting VC (Tab Bar), add UIPopoverPresentationControllerDelegate to your class definition. Then add this function:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
Next, in your Tab Bar VC, add this code to your prepareForSegue method:
if segue.identifier == "popover" {
let controller = segue.destinationViewController as! PopoverVC // whatever you named the class
controller.modalPresentationStyle = UIModalPresentationStyle.Popover
controller.popoverPresentationController!.delegate = self
}
That should do it.

Related

Editing Bar Buttons with PushSegue iOS

I want to have barButtonItems in the navigation bar of a view which I arrive at through a pushSegue.
I tried to achieve this by adding a navigation bar item to my storyboard, but when I launch the app, the barButton doesn't show in the build. See the screenshots here:
How the navigation bar looks on build
How I built it in Storyboards
Flow of the Storyboard
I segue to my MainTextView by clicking on a cell using this code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
if let destination = storyboard?.instantiateViewController(withIdentifier:"MainTextView") as? MainTextView {
destination.post = (posts[indexPath.row])
destination.delegate = self
navigationController?.pushViewController(destination, animated: true)
}
}
}
The back button doesn't even show up in the Storyboard. How could I change the default back button and add additional buttons to the navigation bar programmatically?
I assume this might be a Xcode 11/ios 13 related issue. Click on the segue and change it from push to present modally. From there change presentation context to fullscreen. You might be able to leave the it as push but when you click on your your viewcontroller/navigation controller go to the attributes inspector and look for a option called presentation, and set it to full screen.
Another option to try is to click on the view controllers, go to attributes inspector and under navigation controller - bar visibility tap (shows navigation bar)
Last thing I can think of is that when your segue to and from should be connected to the navigation controller.
Actually a few more possibilities...
1) check your debug view hierarchy when you launch the app. What I mean by this is
https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ExaminingtheViewHierarchy.html
basically its a button you click and you'll be able to see how the views are stacked onto of each other. My best guess is that while you have a bar item button there, the nested button is not actually present/showing.
2) nest your tableviewcontroller in another navigation controller.

Navigation controller with tab bar controller?

I have a tableViewController embedded in my tab bar controller. When a cell is tapped, a segue is launched to another view controller to show the details of that object. However, the Back button is not appearing in the viewDetail. I tried embedding the view into a separate Navigation Controller but that didn't change anything. What am I doing wrong? I currently have Tab Bar Controller -> tableView -> Navigation Controller -> viewDetail (need Back button here to return to tableView).
Here's what I have right now:
Thanks!!
Each UIViewController in UITabBarController could be embedded in an UINavigationController at your convenience, that way you'll be able to use all of the features that you need.
Basically, you need to select the tableViewController, click on Editor menu item, select Embed in and click on Navigation Controller, ta daa.
You can show or hide Navigation Bar if you need it using Interface Builder or programmatically in your Detail viewController as follows:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true
// Do stuff
}
Set NavigationController to TabBarController, then set NavigationController's rootViewController to TableViewController.
You just have the organization wrong. Currently you have Tab Bar Controller -> tableView -> Navigation Controller -> viewDetail. It should be Tab Bar -> tableview -> View detail. Navigation should be separate pointing to table view. Nothing should be pointing to navigation. It should just point to tableview
It should look something like the above picture

Edit button not displayed in UITabBarController's MoreNavigationController

A UITabBarController is being pushed onto the stack:
let presenter = presentingViewController as! UINavigationController
let tabvc = UITabBarController()
tabvc.viewControllers = vcs
tabvc.customizableViewControllers = vcs
presenter.pushViewController(tabvc, animated: true)
Once presented the more tab button correctly shows, but the edit button to rearrange the tab bars does not. According to the docs on the MoreNavigationController:
The interface for the standard More item includes an Edit button that
allows the user to reconfigure the tab bar. By default, the user is
allowed to rearrange all items on the tab bar. If you do not want the
user to modify some items, though, you can remove the appropriate view
controllers from the array in the customizableViewControllers
property.
My guess is that the tab bar is not happy being in a navigation controller. Any ideas on bringing the edit button back?
You can have both a UINavigationController and a UITabBarController ; using Storyboard helps understand the issue better, any of these solutions will work:
Start out with a UITabBarController as initial view controller
Use presentViewController instead of pushViewController
Use a modal Storyboard segue to perform a modal presentation
Swap out the rootViewController dynamically
Initial View Controller Design
When the Tab Bar Controller is initial View Controller, the Edit button is displayed normally.
Pushed Design
Another Navigation Controller is initial View Controller, using one of 5 adaptive Action Segue:
Show
Custom
-> No Edit button, since it is in direct conflict with the parent UITableViewController.
Show Detail
Present Modally
Popover Presentation
-> Edit button displayed as expected.
Code
1. Program Modal
Using the exact code presented in the question, change the last line:
let presenter = presentingViewController as! UINavigationController
let tabvc = UITabBarController()
tabvc.viewControllers = vcs
tabvc.customizableViewControllers = vcs
presenter.presentViewController(tabvc, animated: true, completion: nil)
2. Storyboard Modal
keeping with the Storyboard theme, create a segue of the correct type, assign an identifier (i.e. presentModallySegue) and the 5 lines above become this single line:
self.performSegueWithIdentifier("presentModallySegue", sender: self)
3. root Swap
A more drastic solution involves swapping out the root view controller at the window level:
let tabvc = UITabBarController()
tabvc.viewControllers = vcs
tabvc.customizableViewControllers = vcs
self.view.window!.rootViewController = tabvc
Conclusion
Either change your design to adopt the Tab Bar Controller as the initial View Controller, or present the Tab Bar Controller modally.
The reason is that navigation bar of your presenter overlaps with the navigation bar of More section.
If you don't show the navigation bar for you navigation controller, you will be able to see the Edit button again when you tap on the More tab.

How to name a back button in UISplitViewController

I have UITableViewController (its name is News) and UIViewController (its name is DetailViewController) and UISplitViewController. I want it to show a back button when I use an iPad in portrait orientation. I made the button but I cannot name it. I wrote following code
detailController.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
detailController.navigationItem.leftItemsSupplementBackButton = true
detailController.navigationItem.leftBarButtonItem?.title = navigationController?.topViewController.title
But it doesn't show the name of the button. I see only the arrow (the arrow works).
I also tried the following in my UITableViewController(News) but it didn't help me
I use two segues for different devices with this code.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
var screen = UIScreen.mainScreen().currentMode?.size.height
if (UIDevice.currentDevice().userInterfaceIdiom == UIUserInterfaceIdiom.Pad) || screen >= 2000 && UIDevice.currentDevice().orientation.isLandscape == true && (UIDevice.currentDevice().userInterfaceIdiom == .Phone){
performSegueWithIdentifier("showDetailParse", sender: nil)
self.dismissViewControllerAnimated(true, completion: nil)
} else if (UIDevice.currentDevice().userInterfaceIdiom == .Phone) {
performSegueWithIdentifier("showParse", sender: nil)
}
}
My result on an iPad
My result on an iPhone
Thanks to Paul Hegarty and his invaluable lectures at Stanford University and available on iTunes U... in this case his 2013 lectures under the title "Developing iOS 7 Apps for iPhone and iPad" and specifically "Lecture 11 Table View and the iPad".
If you're using storyboards, then:
Open your main storyboard and select the Navigation Controller that links to the Master View Controller in your Split View Controller group;
Open the Inspector;
Under the heading View Controller, against the property Title, enter the words that you would like to appear alongside the "Back" button chevron.
See screenshot of Master Detail Xcode template set up with a Split View Controller...
If you're instantiating views in code, then:
obtain a reference to the Navigation Controller for the Master View controller;
set the title property of that Navigation Controller with the NSString of words that you would like to appear alongside the "Back" button chevron.
As an aside, I would highly recommend implementation of Auto Layout and Size Classes, that you remove the text for the Back Button property and let size classes determine the appropriate words for your Back Button.
For example, as per the question...
The Solution:
Here is the way to fix the issue with the detail view controller's back button:
For any view controller that gets pushed onto the primary navigation controller's stack, set that view controller's title. (Either in its viewDidLoad: method or in the pushing view controller's prepareForSegue:sender: method.)
Set the primary navigation controller's title in the child view controller's viewDidLoad: method.
For example, in MasterViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:#"Foo"];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[self navigationController] setTitle:[self title]];
}
This will keep the detail view controller's back button title in sync with the top primary view controller's title.
What Is Going On:
UINavigationController, its rootViewController, and UINavigationItem each have a title property.
Note that the back button shown for a current view controller is actually the previous view controller's backButtonItem. (See Figure 1-7 Navigation bar structure)
A UINavigationController will automatically inherit the value of the title of its root view controller, but will not automatically inherit the title of any other controller that gets pushed onto its stack. This is why, by default, the back button of the detail view controller will always show the title of the primary navigation controller's root view controller. You might allocate, initialize, and push multiple child view controllers, but only one navigation controller is allocated and initialized for each side of a standard split view controller.
Additionally, a view controller's navigationItem's title property (whose value will appear in the label in the center of the navigation bar) does not inherit its value from the navigation controller, but from the view controller itself. If you set the view controller's title property to "Bar", and the containing navigation controller's title to "Foo", the label displayed in the center of the navigation bar will say "Bar".

Display UIViewController as Popup in iPhone

Since there is no complete, definitive answer to this common recurring question, I'll ask and answer it here.
Often we need to present a UIViewController such that it doesn't cover full screen, as in the picture below.
Apple provides several similar UIViewController, such as UIAlertView, Twitter or Facebook share view controller, etc..
How can we achieve this effect for a custom controller?
NOTE : This solution is broken in iOS 8. I will post new solution ASAP.
I am going to answer here using storyboard but it is also possible without storyboard.
Init: Create two UIViewController in storyboard.
lets say FirstViewController which is normal and SecondViewController which will be the popup.
Modal Segue: Put UIButton in FirstViewController and create a segue on this UIButton to SecondViewController as modal segue.
Make Transparent: Now select UIView (UIView Which is created by default with UIViewController) of SecondViewController and change its background color to clear color.
Make background Dim: Add an UIImageView in SecondViewController which covers whole screen and sets its image to some dimmed semi transparent image. You can get a sample from here : UIAlertView Background Image
Display Design: Now add an UIView and make any kind of design you want to show. Here is a screenshot of my storyboard
Here I have add segue on login button which open SecondViewController as popup to ask username and password
Important: Now that main step. We want that SecondViewController doesn't hide FirstViewController completely. We have set clear color but this is not enough. By default it adds black behind model presentation so we have to add one line of code in viewDidLoad of FirstViewController. You can add it at another place also but it should run before segue.
[self setModalPresentationStyle:UIModalPresentationCurrentContext];
Dismiss: When to dismiss depends on your use case. This is a modal presentation so to dismiss we do what we do for modal presentation:
[self dismissViewControllerAnimated:YES completion:Nil];
Thats all.....
Any kind of suggestion and comment are welcome.
Demo :
You can get demo source project from Here : Popup Demo
NEW : Someone have done very nice job on this concept : MZFormSheetController
New : I found one more code to get this kind of function : KLCPopup
iOS 8 Update : I made this method to work with both iOS 7 and iOS 8
+ (void)setPresentationStyleForSelfController:(UIViewController *)selfController presentingController:(UIViewController *)presentingController
{
if (iOSVersion >= 8.0)
{
presentingController.providesPresentationContextTransitionStyle = YES;
presentingController.definesPresentationContext = YES;
[presentingController setModalPresentationStyle:UIModalPresentationOverCurrentContext];
}
else
{
[selfController setModalPresentationStyle:UIModalPresentationCurrentContext];
[selfController.navigationController setModalPresentationStyle:UIModalPresentationCurrentContext];
}
}
Can use this method inside prepareForSegue deligate like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
PopUpViewController *popup = segue.destinationViewController;
[self setPresentationStyleForSelfController:self presentingController:popup]
}
Modal Popups in Interface Builder (Storyboards)
Step 1
On the ViewController you want as your modal popup, make the background color of the root UIView clear.
Tip: Do not use the root UIView as your popup. Add a new UIView that is smaller to be your popup.
Step 2
Create a Segue to the ViewController that has your popup. Select "Present Modally".
Two Methods To Create Popup From Here
Method One - Using the Segue
Select the Segue and change Presentation to "Over Current Context":
Method Two - Using the View Controller
Select the ViewController Scene that is your popup. In Attributes Inspector, under View Controller section, set Presentation to "Over Current Context":
Either method will work. That should do it!
You can do this in Interface Builder.
For the view you wish to present modally set its outermost view background to transparent
Control + click and drag from the host view controller to the modal view controller
Select present modally
Click on the newly created segue and in the Attribute Inspector (on the right) set "Presentation" to "Over Current Context"
Feel free to use my form sheet controller MZFormSheetControllerfor iPhone, in example project there are many examples on how to present modal view controller which will not cover full window and has many presentation/transition styles.
You can also try newest version of MZFormSheetController which is called MZFormSheetPresentationController and have a lot of more features.
You can use EzPopup (https://github.com/huynguyencong/EzPopup), it is a Swift pod and very easy to use:
// init YourViewController
let contentVC = ...
// Init popup view controller with content is your content view controller
let popupVC = PopupViewController(contentController: contentVC, popupWidth: 100, popupHeight: 200)
// show it by call present(_ , animated:) method from a current UIViewController
present(popupVC, animated: true)
Imao put UIImageView on background is not the best idea . In my case i added on controller view other 2 views . First view has [UIColor clearColor] on background, second - color which u want to be transparent (grey in my case).Note that order is important.Then for second view set alpha 0.5(alpha >=0 <=1).Added this to lines in prepareForSegue
infoVC.providesPresentationContextTransitionStyle = YES;
infoVC.definesPresentationContext = YES;
And thats all.
Swift 4:
To add an overlay, or the popup view
You can also use the Container View with which you get a free View Controller ( you get the Container View from the usual object palette/library)
Steps:
Have a View (ViewForContainer in the pic) that holds this Container View, to dim it when the contents of Container View are displayed. Connect the outlet inside the first View Controller
Hide this View when 1st VC loads
Unhide when Button is clicked
To dim this View when the Container View content is displayed, set the Views Background to Black and opacity to 30%
You will get this effect when you click on the Button
You can do this to add any other subview to the view controller.
First set the status bar to None for the ViewController which you want to add as subview so that you can resize to whatever you want. Then create a button in Present View controller and a method for button click. In the method:
- (IBAction)btnLogin:(id)sender {
SubView *sub = [[SubView alloc] initWithNibName:#"SubView" bundle:nil];
sub.view.frame = CGRectMake(20, 100, sub.view.frame.size.width, sub.view.frame.size.height);
[self.view addSubview:sub.view];
}
Hope this helps, feel free to ask if any queries...

Resources