Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Glitch when interactively dismissing modal

We have run into this issue when implementing interactive dismissal of a modal view controller (dragging modal down should dismiss it) via UIPercentDrivenInteractiveTransition.

Setup:

  1. setup UIViewController embedded in UINavigationController with at least one button in UINavigationBar
  2. modally present another UIViewController embedded in UINavigationController with at least one button in UINavigationBar
  3. setup UIPanGestureRecognizer on modaly presented UINavigationController to drive UIPercentDrivenInteractiveTransition
  4. drag modal down "holding" it by point on UINavigationBar

Issue:

  • while slowly dragging down, animation glitches causing modal view to jump up and down

  • glitch only appears when :

    1. both UINavigationBars have at least one button on them
    2. you "hold" modal by the point on UINavigationBar

Minimal example can be downloaded from github repo.

Has anyone come accross such an issue? Are there any workarounds? Is there some flaw in our setup?

Update

Issue has been simulated on running project above on iPhone 5 simulator with iOS 9.3, OSX 10.11.4, compiled with Xcode 7.3.1.

Update 2

Further investigation showed, that issue is probably not in the animation: For some reason in given setup pan.translationInView(view) is returning unexpected values which causes animation to jump.

Partial workaround

Based on Vladimir's idea we partially fixed the issue by overriding hitTest method of UINavigationBar:

class DraggableNavigationBar: UINavigationBar {

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        guard let view = super.hitTest(point, withEvent: event) else { return nil }

        if view is UIControl || pointIsInsideNavigationButton(point) {
            return view
        } else {
            return nil
        }
    }

    private func pointIsInsideNavigationButton(point: CGPoint) -> Bool {
        return subviews
            .filter { $0.frame.contains(point) }
            .filter { String($0.dynamicType) == "UINavigationItemButtonView" }
            .isEmpty == false
    }
}
like image 433
Jakub Vano Avatar asked May 13 '16 07:05

Jakub Vano


1 Answers

Very interesting glitch. I found a partial solution of this problem few days ago, and since nobody found a full solution, I'll post this, maybe it will be helpful.

If you override hitTest method of UINavigationBar you can get rid of this issue when you dragging modal by holding on UINavigationBar:

extension UINavigationBar {

    override public func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

        guard let view = super.hitTest(point, withEvent: event) else { return nil }

        if view.isKindOfClass(UIControl) {
            return super.hitTest(point, withEvent: event)
        } else {
            return nil
        }
    }
}

Unfortunately if you drag modal by holding on UIBarButtonItem on UINavigationBar, glitch still be present.

You can also try another approach.

As you noticed, pan.translationInView(view) returns incorrect values which causes animation to jump. You need to compare this value to y coordinate of modal view during dragging. You can get this value by checking presentation layer of the modal view controller:

...

let translation = pan.translationInView(view)

if let layer = view.layer.presentationLayer() {
            print(layer.frame.origin.y)
}

...

You can see that when pan.translationInView(view) starts to show wrong value, layer.frame.origin.y still will be correct in that moment. You can compare these two values and find the pattern when value is incorrect, and change it to correct by adding few points to translation.y value.

like image 102
Vladimir K Avatar answered Sep 29 '22 11:09

Vladimir K