Scroll UINavigationBar on push and scrolling pop - ios

What's the best approach to make UINavigationBar scroll just like other elements in the UIViewController on push or swipe gesture back when using a UINavigationController ?

A UINavigationController subclass that supports pop-interactive UINavigationbar with hidden or show.
See this Library: KDInteractiveNavigationController
From iOS 8 and above, to scroll the UINavigationBar, use the following:
Swift:
class YourViewController: UITableViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
navigationController?.hidesBarsOnSwipe = true
}
}
For more details click here

Related

Popping UIViewController with presented keyboard

I've created UIViewController with UIScrollView in it. In UIScrollView I added some UITextFields for data input. When one of the UITextFields becomes firstResponder (keyboard appeared on the screen) and I'm trying to pop this UIViewController with a swipe gesture, I have the following effect:
View of the UIViewController is getting down and I may see the part of previous UIViewController in current UIViewController. Do you have any ideas, how to solve this?
I have found out that when using custom back actions, the interactive pop gesture stops working.
To fix this you can set the interactivePopGestureRecognizer.delegate property to nil.
Try this:
class NavigationController: UINavigationController, UIGestureRecognizerDelegate {
/// Custom back buttons disable the interactive pop animation
/// To enable it back we set the recognizer to `self`
override func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}

How to fix why status bar space behind ScrollView in Xcode?

I added a scroll view to my view which is hover the status bar (I hid it). The scroll view is working fine, but when I'm scrolling to the top, I have a white space which disappears when I tap on my screen, and appears again when I scroll down then top.
I noticed that the scroll bar is not going to the top of my view, but stopped at the status bar.
Here are screenshots which show you what I mean.
Here I'm at the top of my view but the scroll bar isn't:
Here is the same view with the white status bar which appears when I scroll top again:
It disappear when I tap on my screen or scroll down.
Here are my constraints:
I think it's a problem of Layout Margin or something like that, but I don't what I should change?
I hide the status bar like that in my view controller:
override func viewWillAppear(_ animated: Bool) {
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelStatusBar
super.viewWillAppear(animated)
}
EDIT: Even if I comment the line which hides the status bar, I still have the same problem with my scroll view. So the problem doesn't come from how I hide it.
As Sam said, I changed the content insets to "Never" on the scroll view and it works.
While unrelated to your question, I have to react to the way you hide the status bar - the proper way is to override prefersStatusBarHidden in your view controller and call self.setNeedsStatusBarAppearanceUpdate() in your viewWillAppear:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setNeedsStatusBarAppearanceUpdate()
}
override var prefersStatusBarHidden: Bool {
return true
}
UPDATE
Since your view controller is inside of a UINavigationViewController, you need to override childViewControllerForStatusBarHidden in UINavigationViewController to use visibleViewController as the controller to determine status bar hidden (I added override to childViewControllerForStatusBarStyle for the consistence):
extension UINavigationController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return visibleViewController
}
open override var childViewControllerForStatusBarHidden: UIViewController? {
return visibleViewController
}
}

Display nav bar when user scrolls down - swift

I have created a parallax detail view in swift. Wish to allow nav bar colour and title display when user scrolls down.
Like this example here on the detail view. This is in objective C and I can't figure out the swift version. I'm sure it's simple enough with a few lines of code in the right place.
https://github.com/KMindeguia/movies/blob/master/README.md
I know the nav bar has a .hideswhenuserswips function but can't find anything for this!
Thanks
If you use storyboard for your UINavigationController, you can set like this
Or, you can set in your code like this:
myNavigationController.hidesBarsOnSwipe = true
You can use scrollview delegate methods to show or hide navigation bar.
you can implement scrollViewDidScroll , scrollViewDidEndDecelerating or scrollViewWillBeginDecelerating.
from this delegate methods you can manage your navigation bar.
This component just using simple UIView and implementing UIScrollViewDelegate methods. You can add your custom view in top of parent view and hide it, implement UIScrollViewDelegate methods and track some contentOffset of uiscrollview. Like in this component from lines 237
scrollDelegate methods
Set the NavigationBar in each viewcontroller, if you would not show navigationbar use this code,
self.navigationController?.navigationBarHidden = true
And show the navigationbar in particular viewController put this below code,
self.navigationController?.navigationBarHidden = false
this lines used your method, or you use this code,
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = true
}
override func viewWillDisappear(animated: Bool)
{
super.viewWillDisappear(animated)
self.navigationController?.navigationBarHidden = false
}
when scrolling to show your navigationbar see this link Hide status bar while scrolling
hope its helpful

hidesBottomBarWhenPushed not set when returning back to ViewController

I have the hidesBottomBarWhenPushed = true set for one of my UIViewController's (call it ViewControllerA) that is pushed onto my UINavigationController stack. I also opt to show the bottomBar when I push a new ViewController ontop of ViewControllerA. Therefore I have:
class ViewControllerA: UIViewController {
override func viewWillDisappear(animated: Bool) {
self.hidesBottomBarWhenPushed = false
}
override func viewWillAppear(animated: Bool) {
self.hidesBottomBarWhenPushed = true
}
This all works fine.
When I push ViewControllerA, the bottom bar hides.
When I push any other ViewController, the bottom bar shows.
However, when I am traveling backwards in the navigation stack (aka hitting the UIBarButtonItemBack button), I cannot get the bottomBar to hide when I pop the navigation stack to reveal ViewControllerA.
What am I missing? Thanks!
Got it! Here's what worked:
class ViewControllerCustom: UIViewController {
init() {
self.hidesBottomBarWhenPushed = true
}
override func viewDidAppear(animated: Bool) {
self.hidesBottomBarWhenPushed = false
}
}
And then in every UIViewController's custom implementation of BarButtonItemBack pressed I check to see if the previous view controller (that will be popped to needs to hide the tab bar). Granted I abstracted this out into a general function so I didn't need to repeat code, but here's the concept. Thanks for the help figuring this out though!
func barButtonItemBackPressed(button: UIButton) {
var viewControllers = self.navigationController!.viewControllers as! [UIViewController]
if ((viewControllers[viewControllers.count - 2]).isKindOfClass(ViewControllerCustom.self)) {
(viewControllers[viewControllers.count - 2] as! ViewControllerCustom).hidesBottomBarWhenPushed = true
}
self.navigationController?.popViewControllerAnimated(true)
}
I believe the intended use of this property is to hide the bar when pushed. So, when your view controller appears after the top-most one is popped, it wasn't pushed on the stack, so it doesn't change the tab bar's appearance.
This leaves you with two options:
1) Keep the bottom bar for all view controllers. When text is being entered, the keyboard covers the bottom bar.
2) Hide the bottom bar for View Controller A, as well as any other view controller that is pushed on top of A.

UINavigationController Interactive Pop Gesture Not Working?

So I have a navigation controller in my built for iOS 7 app. The titleView is visible, as well as the back button and navigation bar its self. For some reason, the interactive pop gesture (swipe from the left edge) isn't working. Nothing happens. When I log the gesture, it is not nil. Is there anything special I have to do to enable this functionality? What could cause it not to work?
I have found that when using custom back buttons, the interactive pop gesture stops working (my take is that Apple cannot foresee how your custom back button will behave, so they disable the gesture).
To fix this, as other mentioned before, you can set the interactivePopGestureRecognizer.delegate property to nil.
In Swift, this can easily be done across your entire application by adding an extension for UINavigationController like this:
extension UINavigationController {
override public func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = nil
}
}
Updated answer
Seems like setting the delegate to nil causes the app UI to freeze in some scenarios (eg. when the user swipes left or right on the top view controller of the navigation stack).
Because gestureRecognizerShouldBegin delegate method cannot be handled in an extension, subclassing UINavigationController seems like the best solution:
class NavigationController: UINavigationController, UIGestureRecognizerDelegate {
/// Custom back buttons disable the interactive pop animation
/// To enable it back we set the recognizer to `self`
override func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}
Eh, looks like I just had to set the gesture delegate and implement the following:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Look at this response and comments. All you have to do is set your navigation controller's interactive pop gesture recognizer's delegate to nil:
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
Setting it to a casted self to id<UIGestureRecognizerDelegate> also works because all methods in the protocol are optional, but I think setting the delegate to nil is more appropriate in this case.
My answer is based on Eneko's answer but uses only an extension on UINavigationController and works in Swift 5:
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}
You can put this line in the viewDidLoad method.
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
If you feel you have tried all solutions and stretching your head then you're at the right place.
Goto simulator > Window > Enable Show Device Bezels
Now tried to simulate swipe to back gesture.
The more worked out answer was both Aaron and lojals
First Customise the Navigation controller and then put this code in the class
In ViewDidload put this line:
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
And in class write this function
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES;}
Maybe someone may find this helpful.
If you want to hide the navigation bar but use normal swipe gestures to go back and other navigation controller features, you should use: (navigationBar)
self.navigationController?.navigationBar.isHidden = true
If you want to disable navigation bar (hide navigation bar, disable swipe for back) but want to push viewcontroller you should use: (isNavigationBarHidden)
self.navigationController?.isNavigationBarHidden = true
Update 7-DEC-2018:
Recommended way:
In case that your first controller use hidden navigation bar, but next childs use navigation bar, when you come back to base view controller you will see a black bar in transition in place of navigation bar. This will be fixed very easy if you use in first viewcontroller(parent):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
In Swift 4, I have a UITableView inside my view controller, I solved this issue with:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.interactivePopGestureRecognizer?.delegate=nil
}
Generically add interactive pop gesture to the whole app.
XCODE: 9.0, Swift: 4.0
Preferably create UINavigationController in AppDelegate.swift
Create a navigation controller
// I created a global variable, however not necessarily you will be doing this way
var nvc: UINavigationController!
implement UIGestureRecognizerDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, UIGestureRecognizerDelegate {
Instantiat UINavigationController in application didFinishLaunchingWithOptions function
nvc=UINavigationController()
// For interactive pop gesture
nvc.navigationBar.isHidden=true
nvc?.interactivePopGestureRecognizer?.delegate=self
Extra step, add controller to navigation controller in application didFinishLaunchingWithOptions function
window=UIWindow()
window?.rootViewController=nvc
window?.makeKeyAndVisible()
// BaseViewController is sample controller i created with xib
nvc.pushViewController(BaseViewController(), animated: true)
Implement gusture recognizer, add below code to AppDelegate.swift
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Note: See other post in this section for the difference between
self.navigationController?.navigationBar.isHidden=true
And
self.navigationController?.isNavigationBarHidden = true

Resources