2

I'm attempting to make a view that continuously scrolls an image from right to left, effectively creating a background that scrolls indefinitely. I've built this using two image views next to each other, and using a UIView animation set to .Repeat that translates the frames with a negative horizontal offset. (code below)

It works great! Until I attempt an edge swipe in navigation controller, at which point the animation freezes:

animation freezing on edge swipe

My code looks like:

class ScrollingImageView: UIView {
    required init?(coder aDecoder: NSCoder) { fatalError() }

    let imageView = UIImageView()
    let imageView2 = UIImageView()

    override init(frame: CGRect) {
        super.init(frame: frame)


        imageView.image = UIImage(named: "mario")
        imageView2.image = UIImage(named: "mario")

        imageView.frame = bounds
        imageView2.frame = CGRectOffset(bounds, bounds.width, 0)

        addSubview(imageView)
        addSubview(imageView2)
    }

    func play() {
        UIView.animateWithDuration(5, delay: 0, options: [.Repeat, .CurveLinear], animations: {
            self.imageView.frame = CGRectOffset(self.imageView.frame, -self.bounds.width, 0)
            self.imageView2.frame = CGRectOffset(self.imageView2.frame, -self.bounds.width, 0)
        }, completion: nil)
    }
}

Why does the animation stop when this interaction begins? I'm thinking it has something to do with the interactive gesture setting the speed on the CALayer to zero. Is there a way to make this animation keep playing?

John Gibb
  • 10,603
  • 2
  • 37
  • 48
  • 1
    "I'm thinking it has something to do with the interactive gesture setting the speed on the CALayer to zero" Bingo! That is exactly how an interactive gesture works. – matt Jul 19 '16 at 19:12
  • @matt is there any way for this animation not to participate in the interactive gesture? I've considered using `CADisplayLink` to orchestrate the frames myself, but that seems ugly. Any suggestions? – John Gibb Jul 19 '16 at 19:15
  • I would go the other way: do your own interactive gesture. See my answer... – matt Jul 19 '16 at 19:15
  • Related: http://stackoverflow.com/questions/27810013/limit-effect-of-uipercentdriveninteractivetransition-to-topmost-view – John Gibb Jul 20 '16 at 13:19

1 Answers1

1

As you rightly say, you're using an interactive gesture. How does it work? It freezes the animation (layer speed is zero) and changes its "frame" (layer timeOffset) to match the current location of the gesture.

To work around this, you could perhaps put the animation into a different layer, though I'm not sure about that. The way that I'm sure would work would be to implement the entire custom transition interaction yourself — not using the UIPercentDrivenInteractiveTransition (which freezes the animation) but positioning the view yourself as the transition proceeds.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • That really feels like overkill, since it's just a standard navigation controller & transition. And in my real application, it's just one item in a pretty complicated view tree, so the view controller would be jumping a few layers of abstraction to do something particular for this view. – John Gibb Jul 19 '16 at 19:18
  • Also, I should have included this in the original question, the animation also stops when I send the app to the background, and then bring it back to the foreground. Is that the same issue, or would that warrant another question? (Interestingly—`UIActivityIndicatorView` similarly pauses during an interactive transition, but does *not* freeze when resuming a backgrounded app) – John Gibb Jul 19 '16 at 19:19
  • "Also, I should have included this in the original question, the animation also stops when I send the app to the background, and then bring it back to the foreground. Is that the same issue, or would that warrant another question" No, that's normal. Animations are cancelled when you go into the background. It is up to you resume on coming to the foreground. – matt Jul 19 '16 at 19:38
  • Of course it "feels like overkill". Nevertheless, there is no magic way to stop the runtime from doing what it normally does. If you care enough about this, then what I've suggested is probably what you'll have to do. – matt Jul 19 '16 at 19:41