Fixed Navigation Bar For Different View Controllers - ios

I am trying to implement a navigation bar whose contents persist between different view controllers. For example, I have the following functionality right now:
Non Persistent Navigation Bar
I have set an imageView as the titleView of the navigation bar.
The titleView of the navigation bar transitions along with the view controller here (the image shows some animations by fading in and out). But I would like it to stay hooked onto the top of every screen without any transitions. This would mean that only the part of the view below the navigation bar would show the transition from one view controller to another.
Is that possible in Swift?

Yea that is possible. What you can do is have a container view controller, which can have your navigation bar along with a content view controller.
Now each time you open a new VC, push the new VC on the containerVC's contentVC.
For ex:
let containerVC = self.parentViewController?.containerViewController()
if let _ = containerVC {
containerVC.pushContentViewController(newViewController)
}
Attaching layout screenshot for more understanding.
So if you check here, the Root Container is the view where you can add your new VC as a child VC.

You can do this by changing your UIViewController hierarchy. For this you'll need three view controllers. First will own your UINavigationBar and UIView where other two UIViewController's views will live.
Let's call one with the navigation bar MasterViewController, and other two—ViewControllerA, ViewControllerB respectively.
Somewhere in MasterViewController instantiate child view controllers and add them to your view controller hierarchy. For simplicity's sake let's do everything in viewDidLoad() but you can do do this anywhere you deem it necessary. You could even load view controllers lazily as user demands them.
final class ViewControllerA: UIViewController { }
final class ViewControllerB: UIViewController { }
final class MasterViewController: UIViewController {
var navigationBar = UINavigationBar()
override func viewDidLoad() {
view.addSubview(navigationBar)
let a = ViewControllerA()
let b = ViewControllerB()
addChildViewController(a)
addChildViewController(b)
view.addSubview(a.view)
// you are ready for transitions from a.view to b.view when necessary
}
}
Now you can do transitions from a.view to b.view (and back) and it will affect nothing in master view (which has the navigationBar).
It is important to note that view hierarchy and view controller hierarchy are not liked in any way and you are responsible for managing both.

Related

What is best approach to creating a persistant UIView nav bar in a UITabBarController?

I've added a custom UIView to my base UITabBarController. I start by hiding the default tabBar. The viewdidload looks like this in UITabBarController:
override func viewDidLoad() {
super.viewDidLoad()
//hide default tab bar
self.tabBar.isHidden = true
tabBarArea.frame.size.width = self.view.frame.width
tabBarArea.frame.origin.y = self.view.frame.height - tabBarArea.frame.height
self.view.addSubview(tabBarArea)
}
That works well. The tabBarArea is defined in the storyboard as a custom view for the UITabBarController. The custom view sits between the First Responder and the Exit icons in the top bar.
Now, the problem is that the UITabBarController will disappear as soon as we load a child view controller and this custom UIView area will vanish with it.
Is there a way to make this root custom view always present even when child view controllers are loaded in?
Thanks for input. I like the idea of this custom UIView area but this approach needs refinement. I also don't need it to be a traditional UITabBarController with tab bar items, etc. I'd like to break out of that mold and just have custom UIButtons are whatever in this view area.

How to overlap navigation bar by adding view in swift?

I want to make a custom side bar by adding a new view to the view controller, the side bar will be in the yellow color background. I want my side bar also to overlap the navigation bar/item (green background color) in my view controller. but the navigation bar/item seems can't be overlapped by my side bar view, it seems only overlap the main view.
I tried to find the answer in stackoverflow, I find this Overlap navigation bar on ios 6 with other view, but the answer is on the Objective-C, I can't read Objective-C :(
What should I do to overlap navigation bar/item ? here is the screenshot of my view controller
I embed the navigation controller like this
There are plenty of implementations of slide-over or drawer containers.
What you need to do to get above the navigation bar is CONTAIN the navigation controller inside another view controller.
The stack would look like this.
MasterViewController
UINavigationController
RootViewController
Menu
See this one here:
Swift version of MMDrawerController
You can do this by changing your UIViewController hierarchy. For this you'll need three view controllers. First will contain everything, let's call it MasterViewController; second—your main content with navigation bar; and third—drawer.
In MasterViewController instantiate child view controllers and add them to your view controller hierarchy in viewDidLoad().
final class MasterViewController: UIViewController {
override func viewDidLoad() {
let drawerViewController = DrawerViewController()
let mainViewController = MainContentViewController()
let navigationController = UINavigationController(rootViewController: mainViewController)
addChildViewController(drawerViewController)
addChildViewController(navigationController)
view.addSubview(navigationController.view)
view.addSubview(drawerViewController.view)
}
}
Now you have navigationController.view that you can place or animate anywhere within view.

Swift: present view controller in same context as other view controllers?

Ok, I have an issue that I cant understand trying to present a view controller (the same instance every time, just like other tab item VCs) from an overall tab bar controller VC. My tab bar controller VC has 3 view controllers that it is connected to via storyboard, so 3 tab bar items appear on the tab bar. When the selectedIndex is changed, these view controllers just appear right there below the subviews of the Tab Bar Controller VC.
These subviews that should always be on top are the nav bar at the top and tab bar at bottom:
And this is great for those 3 view controllers. Problem is I need to access 1 instance of ANOTHER view controller that is NOT shown in the tab bar buttons via a button in the nav bar here.
My problem is no matter how I present it, this VC always pops OVER the tab bar controller VC, covering the tab bar and nav bar.
here I make sure only 1 instance is made:
if podcastVC == nil {
//print("IT IS NIL")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
podcastVC = storyboard.instantiateViewController(withIdentifier: "podcast") as! PodcastViewController
//*NOTE: have to set other vars too, this is temp
podcastVC.urlStr = currentTrackUrl!
podcastVC.originalUrl = currentTrackUrl!
AudioPlayerManager.shared.play(urlString: podcastVC.urlStr)
}
self.show(podcastVC, sender: self)
podcastVC.modalPresentationStyle = .currentContext
podcastVC.definesPresentationContext = false
[1]: https://i.stack.imgur.com/1d6MZ.png
as shown by Swift: How to return to the same instance of my UIViewController
How can I make that VC present in the same context as the tab bar items? I have tried setting the layer of the nav bar to a z position much higher (like 10) but nothing works. What is wrong?
Modal view :
Can works for all view controllers
Is over all other view and need to be pop programatically (adding a button back manually for example)
Push View :
Only works in navigation controllers
Add automatically a back button in the navigationController
you should push VC and it will keep tabbar and nav
you can change modal present style

Setting a UINavigationItem.titleView = MyLogoView for all UINavigationViewController children UIViews

My question is technically a duplicate of this SO: How do I set custom navigation bar(title + titleView) for all navigation controllers and view controller?
The base class answer in the link above is my current solution as I will explain below. One problem with this solution though is that it makes my company logo image titleView disappear and reappear for a second with every segue, I am looking to keep the logo visible through all navigation transitions.
I am using a company logo in the center of my UINavigationBar. Currently, for any child UIView of my UINavigationViewController, I add a UINavigationItem outlet in the Xcode 7 Storyboard by CTRL Dragging to my Swift ChildViewController and always use the same ID:
#IBOutlet weak var uiNavItemTitle: UINavigationItem!
I then delete that above Xcode generated line because I have the same line in MyBaseViewController that ChildViewController inherits from. Now I can set the logo one time in MyBaseViewController and every ChildViewController inherits from MyBaseViewController, this code is in MyBaseViewController:
#IBOutlet weak var uiNavItemTitle: UINavigationItem!
override func viewDidLoad() {
super.viewDidLoad()
// MARK: - UINavigationController Defaults
uiNavItemTitle.titleView = CoreUtility.LogoForUINavBarGet()
self.navigationController?.navigationBar.topItem?.title = "";
self.navigationController?.navigationBar.tintColor = UIColor.whiteColor()
}
What is becoming an issue with this solution is that for every ChildViewController in my UINavigationViewController stack of segues, I have to add a UINavigationItem outlet and make sure I inherit MyBaseViewController. Some of my navigation segue views deeper in the navigation chain should not really inherit from this MyBaseViewController though.
Another issue with my solution is that the company logo disappears and reappears a second later for each segue to a new UIViewController
I can create another level to the hierarchy of inheritance is one possible solution.
But my UINavigationItem.titleView is always the same company logo for every child UIViewController segued to.
Do I have to add a UINavigationItem to every UIViewController?
Or is there an "Application" level way such as using, in my AppDeletgate.application():
//MARK: - UINavigationBar.appearance
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
UINavigationBar.somethingForTitleView
Where somethingForTitleView becomes code to set a Application wide Navigation Bar titleView to my logo View.

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".

Resources