andreamazz / AMScrollingNavbar

Scrollable UINavigationBar that follows the scrolling of a UIScrollView
MIT License
6.05k stars 633 forks source link

[strong bug] Error in isUserInteractionEnabled (Please treat preferentially) #332

Closed StefaniOSApps closed 6 years ago

StefaniOSApps commented 6 years ago

Describe the bug Although i have reported many bugs for some days, i really like this framework 👍 !!

Well, I noticed that the UIBarButtonItem sometimes did not work in the UiNavigationBar (because, userInteractionEnabled = false). I did not understand why and it really annoyed me. Now I have found the reason why it is.

To Reproduce

  1. Look down at my small example. >> RUN
  2. First, hide the NavBar by scrolling down
  3. If you just scroll up so far that the NavBar is halfway visible, then you lift the finger off. The scrollbar now expands by itself. The status of userInteractionEnabled is now false. So you can not tap the button on UiNavigationBar.

The bug was created at:

navigationBar.isUserInteractionEnabled = (newState == .expanded)

on line 448 at ScrollingNavigationController.swift

Expected behavior The UIBarButtonItems should always have (userInteractionEnabled = true) after finished scrolling

Example GIT https://github.com/StefaniOSApps/Example

StefaniOSApps commented 6 years ago

It is possible with:

#pragma ScrollingNavigationControllerDelegate
- (void)scrollingNavigationController:(ScrollingNavigationController *)controller willChangeState:(enum NavigationBarState)state{
    if ([controller percentage] < 0.5) {
        [[controller navigationBar] setUserInteractionEnabled:true];
    }
}

to fix the bug until to the next update. Unfortunately, the "willChangeState" function must be used because "didChangeState" is not used in the framework code.

In order to get the delay of the function "didChangeState",

#pragma ScrollingNavigationControllerDelegate
- (void)scrollingNavigationController:(ScrollingNavigationController *)controller willChangeState:(enum NavigationBarState)state{
    if ([controller percentage] < 0.5) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [[controller navigationBar] setUserInteractionEnabled:true];
        });
    }
}

can also be used.

do not forget: [(ScrollingNavigationController *)self.navigationController setScrollingNavbarDelegate:self];

many thanks to @Dave

DaveMcKraken commented 6 years ago

After playing around with it, I found that if I used the same calculation for threshold that the pod uses, that the timing is a little more accurate.

Example (Swift 4.2):

func scrollingNavigationController(_ controller: ScrollingNavigationController, willChangeState state: NavigationBarState) {
    let statusBarHeight = max(UIApplication.shared.statusBarFrame.size.height, UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0)
    let threshold = statusBarHeight - (controller.navigationBar.frame.size.height / 2)
    if controller.navigationBar.frame.origin.y >= threshold {
      controller.navigationBar.isUserInteractionEnabled = true
    }
  }
StefaniOSApps commented 6 years ago

The best would be if the function controller.navigationBar.isUserInteractionEnabled = true is inserted in a completion block of UIView.animate.

OR

use: scrollingNavigationController(controller:didChangeState state:),

after the implementation of it (see bug #333)

StefaniOSApps commented 6 years ago

What's the reason that controller.navigationBar.isUserInteractionEnabled is set to false? Is this even necessary? @DaveMcKraken

DaveMcKraken commented 6 years ago

From what I can tell, the state is stuck on .scrolling and navigationBar.isUserInteractionEnabled = (newState == .expanded) is always evaluating to false unless the bar is fully dragged down to be .expanded

It looks like a problem with checkForPartialScroll and a rounding error on the nav bar origin or something. I'd agree that putting something in the UIView.animate completion block like you suggested would likely fix this problem.