UIScrollView's setContentOffset called before new subview is loaded - iphone

I have a UIView that I'm sliding in using a UIScrollView. Code looks like this:
[view setObject:object]; // updates a bunch of labels, images, etc
[scrollView addSubview:view];
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
Problem is, the scrollView doesn't seem to wait for the view to be completely loaded before animating its contentOffset, and so the animation is kinda jerky (almost non-existent on the first slide-in and on older devices). Oddly enough, switching the 3rd line to this fixes it:
[UIView animateWithDuration:0.3 animations:^{
[scrollView setContentOffset:CGPointMake(320,0) animated:NO];
}];
No lag whatsoever, perfectly smooth slide-in. However, this doesn't trigger any of the UIScrollViewDelegate calls, on which I also depend (but which aren't responsible for the lag).
Any idea of how I could tell the UIScrollView to wait for the view to be completely loaded before animating its contentOffset? Or maybe there's something else that I'm missing?
EDIT: before anyone else suggests it: yes, I did try:
[self performSelector:#selector(slideIn) withObject:nil afterDelay:1];
just to see if it would fix it. And yes it does fix the lag, but this is not an actual solution. performSelector:afterDelay: is never a solution, it's only a superficial fix. Plus, you're making the user wait extra seconds every time (since the actual delay may be much shorter than 1 second depending on the device model).

Performing selector after delay 0.01 works because the invocation is scheduled on the runloop vs. calling the method immediately. For that matter, afterDelay:0.0 may also work. Did not try in this particular case but works in may similar situations.

have you tried starting the scrolling in the "viewDidAppear" function of the viewcontroller... maybe this is called when the loading is finished.
otherwise try afterdelay with a very short time like 0.01 .. so that the call is scheduled next after the current work is done.

Try this, set contentSize after some delay (here delay is 2 second).
[view setObject:object];
[scrollView addSubview:view];
[self performSelector:#selector(contentSize) withObject:nil afterDelay:2];
}
-(void) contentSize {
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
}

Related

Delay between viewDidLoad and viewDidAppear with animated UIImageView

I have a controller that uses an animated UIImageView to display a sequence of 30 512 x 512 frames. When I run the application the view quickly does the following.
- (void)viewDidLoad {
[super viewDidLoad];
[[self imageView] setAnimationImages:[[self dataModel] framesForLOOP]];
[[self imageView] setAnimationDuration:2.5];
[[self imageView] setAnimationRepeatCount:1];
[[self imageView] startAnimating];
NSLog(#"MARKER_001");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"MARKER_002");
}
This all works fine but what I am trying to work out is that after viewDidLoad: is called there is a 2 second delay before viewDidAppear: is called (between MARKER_001 and MARKER_002).
I was thinking there might be a delay setting up the frames NSArray or after calling setAnimationImages: or maybe after startAnimating.
Is there anyway to reduce/remove this delay, I would prefer to preload the animation at startup and take a hit there rather than having the delay when the viewController loads as it makes the button that fires the segue to instantiate the new controller feel laggy.
Just a few ideas:
reduce the pain - do you need 30x512x512?
distribute the pain - load the first image on viewWillAppear, kick off an operation to load the others and update the animation images as new images are ready (can supply code e.g. if needed)
move the pain - prepare the array of UIImages in app init.
dig deeper - let's have a look at the framesForLoop method, maybe there's some more opportunity to reduce/distribute/move the pain in there.
Note that even if you call startAnimating in viewDidLoad, the animation won't start there. You can't get an animation running in a controller which has not been not displayed yet.
You should call the startAnimating in viewDidAppear instead.
Two seconds delay between these two methods is not anything strange. The delay can be much longer or shorter, depending on what happens inside your application.
Measuring the time between two methods which are not connected doesn't make sense. What about measuring how much time the individual methods take?
If anything is laggy, you should probably paste all the code that happens between the user action and the moment when everything is displayed and the lag happens.

CATransform3D weird UIScrollView contentOffset

I'm doing a cool CA3DTransform while a UIScrollview is scrolling in the scrollViewDidScroll delegate method. It works perfectly when you use your finger to scroll, so when scrolling manually everything is perfect.
But, when I set scrollview contentoffset programmatically like:
[scrollView setContentOffSet:CGPointMake(0,460) animated:YES];
It still calls the delegate method scrollviewdidscroll, so the same animation methods are called, so I still see the correct animation, BUT, somehow parts of the view are missing during and after the animation! I've tried to set the layer.zPosition on all things and it doesn't seem to help. It should be fine since manually scrolling does work without parts of views going missing... Somehow calling this method programmatically differs and I have no idea why!
Post your question in this way is not very clear, considering that with the delegate scrollViewDidScroll, I can print me the code, either manually or programmatically.
...
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 10, 320, 200)];
[sv setContentSize:CGSizeMake(640, 200)];
sv.delegate = self;
[sv setContentOffset:CGPointMake(320, 0) animated:YES];
[self.view addSubview:sv];
...
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"---%#---", NSStringFromCGPoint(scrollView.contentOffset));
}
In this simple example, you can see that the delegate actually works.
Excuse the simplicity of the answer, but the only question you ask is hard to give you help.
I had a similar problem (in the context of using a scrollview for a slideshow) but solved it by using scrollRectToVisible:NO (NO for no animations), and wrapped that inside a UIView animation block where I also put my animation code.

Total mystery - iOS methods execute after random delay

I have the variant of the following codes, triggered by a user input.
NSLog(#"WHY YOU iOS!!!");
[scanButton setSelected:YES];
[overlayImageView setImage:image];
[overlayView setHidden:YES];
The thing is that the above routine executes with seemingly random delay from five seconds to more than a minute.
Sometimes, they don't execute at all, except, mysteriously, the NSLog method. If I set a breakpoint at the NSLog line, it just executes immediately and I can see "WHY YOU ..." printed out in the console. However, even though the debugger runs smoothly past the below three lines without errors or warnings, the effect of the the next three lines takes place after random delay, or infinite delay. What's wrong? How could it be possible that they have random delay?
I suspected that UI part of Cocoa SDK suspends due to my poor program design or whatever, but even during the random delay, other buttons and functionalities work so fine, while the effect of setImage takes place suddenly in 47.2 seconds. What's wrong with iOS, or with me?
What confounds me more is that the above exact routine works fine without delay if the image parameter (UIImage*) comes from a different source. If that's the case, we can attribute the cause of the problem to the image, however, why do scanButton and overlayView also respond with the same delay, even though they have nothing to do with the image? Total mystery to me.
I have no idea on whats going on, as there is almost no hint in the code that you've provided, but why you dont try something like this:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"WHY YOU iOS!!!");
[scanButton setSelected:YES];
[overlayImageView setImage:image];
[overlayView setHidden:YES];
[overlayImageView setNeedsDisplay];
[overlayImageView setNeedsLayout];
[scanButton setNeedsDisplay];
[scanButton setNeedsLayout];
[overlayView setNeedsDisplay];
[overlayView setNeedsLayout];
});
Basically ensure always to update de UI in the main thread (in case that you're using a background thread) and set the UI controls as needed to be redrawn.

UIView hidden property...is there more to it?

Coming from ActionScript, I would set Sprites to visible=false in order to keep them from being calculated in things like layout, and to ensure they would not respond to events.
In iOS development, I am continuing with this--if a UIView is not needed, I may both animate its alpha to zero and then set hidden=true. I wanted to know if I was wasting my time, or if there is a benefit to this. In my current project, I'm doing so with UIImageViews which are not responding to events anyway.
Is setting hidden to true good practice, or or just additional overhead?
This is the best choice, because setting hidden to true removes view from the render loop. While setting alpha to 0 just makes view transparent.
If you don't need it any more, then you should properly remove it from memory. In that case you would just animate its alpha (to make it look good), then dealloc it.
if you autoreleased it, then all you have to do is remove it from the superview and its retain will hit 0 and be dealloced.
[UIView animateWithDuration:.5
animations: ^ {
[myView setAlpha:0];
}
completion: ^ (BOOL finished) {
[myView removeFromSuperview];
}];

How to force screen update for UIScrollView from non-UI thread/function (iPhone)

I am drawing a set of images on the uiscrollview from a non-ui thread/function. But its only displayed after all the images are done drawing. For drawing all the images, I have written a function and that is what is being called as the non-ui thread. I did write this line inside the function
[self performSelectorOnMainThread:#selector(updateUI) withObject:nil waitUntilDone:NO];
And have written the function as given below
- (void)updateUI
{
[myScrollView setNeedsDisplay];
}
But its having no effect even when I can see the control being passed to that function. What to do now?
In my experience all drawing operations must be performed on the UI thread, even if they are not visible, or even added to the view hierarchy, when you are doing them.
If you find a way around that I'd be keen to know, though :-)
Ok, I got a solution. Its working perfectly even though I dont like the solution. I changed my function like this.
- (void)updateUI
{
for(UIView *subview in [myScrollView subviews])
{
[myScrollView bringSubviewToFront:subview];
}
}
Maybe you should also call setNeedsLayout and/or layoutIfNeeded?
Try something like performSelectorOnMainThread:withObject:waitUntilDone: