Search code examples
iosmultithreadingnsoperationnsoperationqueueuiviewanimation

NSOperation that will operate on UIView launches before layoutSubviews is called


Say I have a subclass of UIView which I will call AnimatableView, where my implementation of layoutSubviews lays out some internal content.

AnimatableView also has the method commenceAnimationLoop which looks something like this:

- (void) commenceAnimationLoop {

    NSOperation *animationLoop = [[AnimationLoopOperation alloc]init];
    [queue addOperation:animationLoop]; //queue is a NSOperationQueue iVar of AnimatableView
    [animationLoop release];
}

The AnimationLoopOperation is a subclass of NSOperation whose main method will start an infinite animation which only exits once the isCancelled property becomes YES.

The problem I am having is in my ViewController where I setup and animate an instance of AnimatableView like so:

- (void) viewDidLoad {
    AnimatableView *animatableView = [AnimatableView alloc]initWithFrame:someFrame];

    [self.view addSubview: animatableView];
    [animatableView commenceAnimationLoop];

    [animatableView release];
}

The problem is that the NSOperation created in the commenceAnimationLoop executes before my instance of AnimatableView has had chance to call layoutSubviews (meaning that it's content has not yet been generated and laid out).

I am making an educated guess that the layoutSubviews method is called later (when the ViewController is ready to render its view hierarchy?), but that the commenceAnimationLoop method spawns a thread that immediately executes in the background.

To summarise - what would be the best way to make my animationLoop operation wait for the view to layout before commencing?

To provide some high level context for what I am trying to achieve - I am using NSOperation to start a continuous animation. The idea being that I can provide an endAnimation method which would likely call cancelAllOperations on my queue. All this is done in the hopes I can provide a way of starting and then interrupting an infinite animation with the two methods commenceAnimationLoop and endAnimation. Perhaps I am barking up the wrong tree going down the route of multi-threading?


Solution

  • If you need layoutSubviews to run before your operation then you should't start the operation queue before it that. You can prevent an operation queue from starting additional operations using [myOperationQueue setSuspended:YES];.

    Since it will only prevent the queue from starting new operations you should suspend the queue before you add your operations to it.

    After you view has loaded and layoutSubviews has run you can resume the operation queue again using [myOperationQueue setSuspended:NO];. One place to do this could be in viewDidAppear.