I am new to IOS Swift development. I have a navigation bar design which includes, increasing the height (thus increased text size with custom colors), custom UIButton for closing (instead of the usual back button)
and title at the left side (instead of center)
Basically a lot of customization to do. My question is, is it okay to do a custom UIView to act as a navigation bar or should I push through with a NavigationController and just customize it via code?
Thank you.
First of all the navigation bar offer the push navigation through different view controllers in a smarter way, it stacking all the view controllers pushed and it offers some useful features; for example pushing another view controller from storyboard you don't have the need to set the back button and you can come back to the main controller in a simple way.
You can set a custom image for left/right button, set custom fonts and also change the height without big problems; I suggest to keep the navigation bar and evaluate, you should discover in a short time if a nav bar is enough for your needs.
Related
I have to add new screens to an existing app, that will use a different navigation bar style than the one that already exists and will be accessed from different screens. The idea would be to show the new Navigation Bar style only for those screens, so when the user finishes that flow or goes back to the screen that started the navigation, it should show again the navigation bar style it was previously using.
I have tried 2 things but didn't work as expected:
I thought of wrapping the new screens in a new UINavigationController, so I could change its navigation bar style and it would be consistent for the new screens. It works but the problem is that I am not able to customize the initial UINavigationController transition to make it not look like a modal (i.e I want to show that UINavigationController with the same animation as if I were pushing a UIViewController). Is there a way to do that? By the way, I am managing the navigations with storyboards segues.
I also thought of using
self.navigationController?.navigationBar.isHidden = true
But doesn't seem clean because I would have to show it again when the flow is finished (it is a bit long) or cancelled. This makes a lot of combinations and it would be easy to miss one of them, so this doesn't seem a practical solution. Is there a better way to do this?
The presenting view controller is where you want to change the nav bar for the controller to be presented. So you'd make any changes to the navbar just before you call push(viewController:animated:).
And then in viewWillAppear of still the presenting view controller you would reset the navbar to what it was initially.
NOTE: Keep in mind that depending on the kind of changes you're doing to the navbar, the transition might no longer be smooth. The user might see some flickering of sorts on the navbar, which would be poor UI/UX.
Edit: Another alternative
Alternatively, you could get rid of the navbar and instead implement your own header view that you can style however you want, to represent the navbar.
You can style it as a simple navbar with a title + back button.
You can style it to look like a navbar with large title
You can style it as a navbar with back button + background image + title + right bar buttons
As you can see, this second alternative offers you more freedom in what you can do.
I have a simple Navigation View Hierarchy that has 2 views it goes between. I wanted a customized navigation bar, so I have the default one hidden, and I've implemented a Container View which is shared between the 2 views in the nav hierarchy.
Everything works as I want it to, except when I segue to the lower or higher view the top bar appears slides away and reappears on the new view. I would like it to appear stationary when I push or pop to other views in the hierarchy.
Is there an easy way to do this? Or should I delete my custom shared Container View and try to make this work with the Navigation Bar (which I have currently "hidden")?
I had to do this for a client once. The way we did it was, like you said, make an encompassing view controller that housed a container view. Within this container view, we embedded a UINavigationController and would manually pop and push UIViewControllers to its navigation stack. Of course you want to hide the UINavigationController's nav bar.
It sounds like you sort of implemented this, but instead you just embedded a plain old view controller inside your custom navigation controller, and then segue to another view controller that is also embedded in the custom view controller? Ideally you want one instance of this custom nav controller with an embedded UINavigationController. I believe you will have to do all the view controller transitions programmatically.
Opinion: Personally, I would recommend against doing this. I believe that an app should feel like an extension of the OS it's on. A user should feel it's a part of their phone. Using the native navigation bar also decreases the level of effort a user is required to put forth to understand your app.
I know you're thinking "but it's just a nav bar" but we're talking about the same people that will potentially uninstall an app if it takes longer than 2.5s to load.
I wanted a customized navigation bar, so I have the default one hidden
That's your mistake. The way to get a customized navigation bar in a UINavigationController interface is to initialize it with init(navigationBarClass:toolbarClass:). Now the built-in navigation controller is using your navigation bar! And from there on, all will be well.
https://developer.apple.com/reference/uikit/uinavigationcontroller/1621866-init
I followed this blog post that explains how you can implement a custom UINavigationBar that has an increased height, if for example you wanted to put additional ui elements in the nav bar underneath the rest of the bar content that will persist between navigation on the stack. This code works really well in the case where you always want it to be that increased height.
In my app, I need to start the navigation bar at its default height, then increase it later, adding more content, after the user performs a given action. Very similar to the song info and controls in the iTunes Store:
So I put some checks in place to not reposition anything if a BOOL property is NO. When I set it to YES, I call [self setNeedsDisplay] which will call layoutSubviews to position everything appropriately based on that boolean value. sizeThatFits is also called and I return the proper height.
The problem is, I can't call [self setTransform:CGAffineTransformMakeTranslation(0, -(NavigationBarHeightIncrease))]; in initialize. Instead I call that at the same time I change the boolean value to YES. Because of this, all of my elements are moved up that amount. But if I don't call setTransform, the elements in the nav bar are in the proper position, but the bar itself is positioned too far down, so that the custom view I've added to the bar is shown overtop the view controller's view - it bleeds out, and the extra space I added is black not the navigation bar's background color.
If I call setTransform in initialize, when the height is the default height, the elements are moved up when they shouldn't be.
So, how can I properly dynamically change the height and positioning of a UINavigationBar subclass?
As suggested in the comments, to achieve the behavior where a custom navigation bar (not subclassing the native control) persists across pushes and pops of controllers in a navigation controller, you'll need to have a single controller with the custom navigation bar and then a single embedded view that resolves to a UINavigationController with its view controllers underneath. Then, it will also be necessary to set the navigation controller's delegate to the root controller so that the title and other properties can be updated as the sub-controllers are pushed and popped. I've provided a screenshot below of what the storyboard version of this might look like:
An option is to create a UIViewController in storyboard which has only the control view you wish to show below the navigation bar with everything else transparent. The advantage here is you design this using the normal tools. Use constraints to place it just below the navigation bar and to set the heights, widths etc of your views.
When you wish to show the control, you can create an instance of this UIViewController and remove the content view from it and add it to the view hierarchy of what is on screen.
There are two options for inserting the extracted base view:
If you add this control view to the view controller at the top of
the navigation stack (what is on screen), it would be covered when
you push on a new controller. This is not what you said you wanted.
If you add this control view to self.navigationController.view, then
it will persist across pushes and pops. This is what you said you wanted.
I use this approach to provide popup help bubbles to describe what is on screen. Depending on whether I use option 1 or 2, I can persist help across multiple pushes/pops.
I got the idea from this tutorial which describes the general approach: http://blog.typpz.com/2013/12/09/ios-sdk-create-a-pop-up-window/
That link provides a full code example on how to bring up the view and remove the view.
This would let you design it in IB, present and dismiss it as required and persist it across navigation sequences.
Hope this helps.
I'm trying to create a tabbed application with navigation elements inside the tab bar, as seen in the picture below (the red bar) using Swift/XCode 6.2. Basically those three icons in the middle will direct the user to different view controllers. The other two icons would be context-based. For example, on a table view page you would see the menu icon and add new icon as seen in the image. However, clicking on a row would change the menu icon to a back icon, and the add icon to something else.
That's the general idea, but I'm having a very hard time implementing something even close to this. The first issue is that whenever I embed a view in a Tab Bar Controller, I can't move the tab bar to the top. However, when I create a custom UITabView in a View Controller, Control + Click and dragging a Tab Bar Item to another view doesn't create a segue. I haven't even begun to tackle having the navigation elements inside the bar.
I guess what I'm asking is just for a little guidance on what route to take to tackle this. I'm assuming I can't use a Tab Bar Controller or Navigation Controller because it doesn't seem like I can customize them all that much. So custom Tab Bar and Navigation Bars, and then implemnt the segues and button changes programmatically?
Thanks.
I will try to guide you from an architectural perspective (so you won't find much code below).
Using a UITabBarController
In order to achieve what you are suggesting, you are right you cannot use a UITabBarController straight away, among several reasons, the most immediate one is that they are meant to be always at the bottom and you want it in top (check Apple's docs). The good news is that probably you don't need it!
Note: If you still want to go with a UITabBarController for whatever reason, please see #Matt's answer.
Using a UINavigationController
You can use a UINavigationController to solve this task, since the UINavigationBar of a UINavigationController can be customized. There are multiple ways on how you can organize your view's hierarchy to achieve what you propose, but let me elaborate one option:
To customize a UINavigationBar's to add buttons, you just need to set its navigationItem's title view:
// Assuming viewWithTopButtons is a view containing the 3 top buttons
self.navigationItem.titleView = viewWithTopButtons
To add the burger menu functionality on a UINavigationController you can find several posts on how to do it and infinite frameworks you can use. Check this other SO Question for a more detailed answer (e.g. MMDrawerController, ECSlidingViewController to mention a couple).
About organizing your view hierarchy, it really depends on if when the user taps one of the main top buttons, it will always go to the first view controller in the new section or if you want to bring him back to the last view in the section where he was.
3.1 Switching sections displays the first view of the new section
Your app's UIWindow will have a single UINavigationController on top of the hierarchy. Then each of the 3 top buttons, when tapped, will change the root view controller of the UINavigationController.
Then, when the user changes section, the current navigation hierarchy is discarded by setting the new section view controller as the UINavigationController root view controller.
self.navigationController = [sectionFirstViewController]
3.2 Switching sections displays the last displayed view in the new section
This will require a slightly modified version of the above, where your each of your sections will have its own UINavigationController, so you can always keep a navigation hierarchy per section.
Then, when the user taps one of the top buttons to switch section, instead of changing as previously described, you will change the UIWindowroot view controller to the new section's UINavigationController.
window.rootViewController = sectionNavigationController
Using a custom implementation
Of course, the last and also very valid option would be that you implement yourself your own component to achieve your requirements. This is probably the option requiring the biggest effort in exchange of the highest customizability.
Choosing this option is definitely not recommend to less experienced developers.
I'd like to take a stab at this--I think it is possible to use a tab bar controller here.
Your topmost-level view controller will be a UITabBarController with a hidden UITabBar.
Each tab is contained in a UINavigationController.
All view controllers in the navigation controller will be a subclass of a view controller (say, SwitchableViewController).
In SwitchableViewController's viewDidLoad, you set the navigation item's title view (i.e. whatever's at the center; self.navigationItem.titleView) to be the view that holds the three center buttons. Could be a UISegmentedControl, or a custom view.
Whenever you tap on any of the buttons, you change the topmost UITabBarController's selected index to the view controller you want to show.
Issues you may encounter:
Table views inside tabs will have a scrollIndicatorOffset at the bottom even if the tab bar is hidden.
Solution: Play around with the automaticallyAdjustsScrollViewInsets of the tab bar controller, or the inner view controller. https://stackoverflow.com/a/29264073/855680
Your title view will be animated every time you push a new view controller in the navigation stack.
Solution: Take a look at creating a custom transition animation for the UINavigationController.
I'd like to use a slide up / down effect to display various viewControllers inside a navigation controller. A few other apps do this like square, and every day. Basically when the app loads, I want to display a base view in the navigation controller. Then slide up another view controller over top of it. When then hit a button in the nav bar, I want to slide that down and show the base controller, all the while retaining the navigation bar, and changing up nav items.
Originally I tried to make this work by showing a modal but that requires using a new nav bar.
Has anyone done this, or knows of a good example that illustrates this UI pattern? Thank you!
You could have UIViews in a UIScrollView