Adding a UIActivityIndicator to a modal view (ELCimagepicker) - iphone

I've added the ELCimagepicker (https://github.com/Fingertips/ELCImagePickerController) to my project and it works perfectly, allowing the user to select multiple images for a slideshow. But when you click 'Save', there can be a lengthy delay depending on how many photos were added.
I've been trying to add a UIActivityIndicator when the user clicks 'Save', but having trouble due to the modal view that is presented. I can call a method from the activity that ELCimagepicker presents (ELCImagePickerController) and this gets actioned by the activity handling the presenting of the image picker. But whenever I try to add to the view, it isn't shown as the modal is on top of the activity indicator.
I've tried using bringSubviewToFront, I've tried adding the code directly to the imagepicker method file with [[self parentViewController] addSubView], but no luck.
Here's the latest code I tried: (indicator is declared in the .h file as UIActivityIndicator *indicator)
indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
indicator.hidden=false;
[self.navigationController.view addSubview:self.indicator];
[self.navigationController.view bringSubviewToFront:self.indicator];
[indicator startAnimating];
if([delegate respondsToSelector:#selector(elcImagePickerController:showIndicator:)]) {
[delegate performSelector:#selector(elcImagePickerController:showIndicator:) withObject:self withObject:#"test"];
}
Has anyone had any success with adding a UIActivityIndicator on top of the ELCimagepicker, or another modal view handled by another class?
I've tried MBProgressHUD but couldn't get that working quite right either - it would show up when I used it in the ELCimagepicker class, but crashed on removal with:
bool _WebTryThreadLock(bool), 0x42368e0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
Any help would be fantastic.
Thanks.

I have figure out your problem. You can do this as below..
-(void)selectedAssets:(NSArray*)_assets {
UIActivityIndicatorView * activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIViewController * topView = [self.viewControllers objectAtIndex:[self.viewControllers count]-1];
activityIndicator.center = CGPointMake(topView.view.frame.size.width/2, topView.view.frame.size.height/2);
[activityIndicator setHidden:NO];
[topView.view addSubview:activityIndicator];
[topView.view bringSubviewToFront:activityIndicator];
[activityIndicator startAnimating];
[self performSelector:#selector(doProcess:) withObject:_assets afterDelay:2.1];
}
- (void) doProcess:(NSArray *)_assets {
NSMutableArray *returnArray = [[[NSMutableArray alloc] init] autorelease];
for(ALAsset *asset in _assets) {
NSMutableDictionary *workingDictionary = [[NSMutableDictionary alloc] init];
[workingDictionary setObject:[asset valueForProperty:ALAssetPropertyType] forKey:#"UIImagePickerControllerMediaType"];
[workingDictionary setObject:[UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]] forKey:#"UIImagePickerControllerOriginalImage"];
[workingDictionary setObject:[[asset valueForProperty:ALAssetPropertyURLs] valueForKey:[[[asset valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]] forKey:#"UIImagePickerControllerReferenceURL"];
[returnArray addObject:workingDictionary];
[workingDictionary release];
}
[self popToRootViewControllerAnimated:NO];
[[self parentViewController] dismissModalViewControllerAnimated:YES];
if([delegate respondsToSelector:#selector(elcImagePickerController:didFinishPickingMediaWithInfo:)]) {
[delegate performSelector:#selector(elcImagePickerController:didFinishPickingMediaWithInfo:) withObject:self withObject:[NSArray arrayWithArray:returnArray]];
}
}
Let me know if this answer help you ...
Thanks,
MinuMaster

It looks like you are updating UI on a background thread. All UIKit updates are to be done in the main thread. So I recommend you execute methods doing UI updates using performSelectorOnMainThread:withObject:.

I solved the issue like this
activityIndicatorObject = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
// Set Center Position for ActivityIndicator
activityIndicatorObject.center = CGPointMake(150, 250);
activityIndicatorObject.backgroundColor=[UIColor grayColor];
// Add ActivityIndicator to your view
[self.view addSubview:activityIndicatorObject];
activityIndicatorObject.hidesWhenStopped=NO;
[activityIndicatorObject startAnimating];

Related

MBProgress HUD crashing application on reloadData on tableView

I am using this piece of code to display an MBProgressHUD over the top of one of my views while I download data from a web-service, the only problem is that occasionally this code will cause the app to hang, doing nothing while the HUD displays "Downloading" and the screen is locked. Also, if I have something like a keyboard being displayed to the user when I press the refresh button (refresh button performs the download) then the application crashes on the line:
[self.tableView reloadData];
My code:
//Checks for network connection then displays HUD while executing pullAndDisplayData method
- (IBAction) update {
UIAlertView *errorView;
if([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) {
errorView = [[UIAlertView alloc]
initWithTitle: #"Network Error"
message: #"No Network connection availible!"
delegate: self
cancelButtonTitle: #"OK" otherButtonTitles: nil];
[errorView show];
}
else
{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Downloading";
HUD.minSize = CGSizeMake(135.f, 135.f);
[HUD showWhileExecuting:#selector(pullAndDisplayData) onTarget:self withObject:nil animated:YES];
}
}
//Downloads this users data from the web-service
- (void) pullAndDisplayData{
// Indeterminate mode
ExpensesDataDownloader *downloader = [[ExpensesDataDownloader alloc] init];
[downloader pullAndDisplayData];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults objectForKey:#"canExportCSVServer"] isEqualToString:#"1"])
{
}
[self.tableView reloadData];
// Switch to determinate mode
HUD.mode = MBProgressHUDModeDeterminate;
HUD.labelText = #"Updating";
float progress = 0.0f;
while (progress < 1.0f)
{
progress += 0.01f;
HUD.progress = progress;
usleep(15000);
}
// The sample image is based on the work by www.pixelpressicons.com, http://creativecommons.org/licenses/by/2.5/ca/
// Make the customViews 37 by 37 pixels for best results (those are the bounds of the build-in progress indicators)
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Completed";
sleep(2);
}
Any help would be much appreciated.
Jack
pullAndDisplayData method is running on a separate thread. This is so that MBProgressHUD can use UI thread to show itself. You should always update your UI from main (UI) thread. Use performSelectorOnMainThread: method to call [self.tableView reloadData]; and other UI stuff. I am assuming that [downloader pullAndDisplayData]; is synchronous call.
From MBprogressHUD API
/**
* Shows the HUD while a background task is executing in a new thread, then hides the HUD.
*
* This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
* pool.
*
* #param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
* #param target The object that the target method belongs to.
* #param object An optional object to be passed to the method.
* #param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
* animations while (dis)appearing.
*/
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
Since you are using this method, your pullAndDisplayData is executed in a new thread. This could cause the strange problem you have (I suppose). You are updating the UI elements from a background thread and this is not good. UI elements would be updated form the main thread. Use the background thread to downlaod data only.
Insetad of using it, try to use GCD (Grand Central Dispatch)
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// download operation here...
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
// reload data here...
});
});
For further info see Usage section for the MBProgressHUD.
The problem might be occurring due to some memory issue, You try using SVProgressHUD for this, it is the extended version of MBProgressHUD:
You just need to do like this :
- (void) pullAndDisplayData{
[SVProgressHUD showWithStatus:#"Downloading..."];
// Indeterminate mode
ExpensesDataDownloader *downloader = [[ExpensesDataDownloader alloc] init];
[downloader pullAndDisplayData];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults objectForKey:#"canExportCSVServer"] isEqualToString:#"1"])
{
}
[self.tableView reloadData];
// Switch to determinate mode
HUD.mode = MBProgressHUDModeDeterminate;
HUD.labelText = #"Updating";
float progress = 0.0f;
while (progress < 1.0f)
{
progress += 0.01f;
HUD.progress = progress;
usleep(15000);
}
// The sample image is based on the work by www.pixelpressicons.com, http://creativecommons.org/licenses/by/2.5/ca/
// Make the customViews 37 by 37 pixels for best results (those are the bounds of the build-in progress indicators)
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Completed";
sleep(2);
[SVProgressHUD dismiss];
}
No need to allocate or release anything. It works just like that!!!
All the best!!!

UIActivityIndicatorView not spinning

I have this problem where I am adding an UIActivityIndicatorView to a UIScrollView; Everything is working fine, except it does not start spinning unless the UIScrollView is scrolled.
Can anyone help me with this issue?
Thank you.
here is some code:
UIActivityIndicatorView *loaderActivity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
loaderActivity.center = CGPointMake(87/2,y+56/2);
loaderActivity.tag=tag;
[mainScrollView addSubview:loaderActivity];
[loaderActivity startAnimating];
[loaderActivity release];
You need to call startAnimating on the activity indicator to have it animate. Alternatively in interface builder you can tick the "animating" tickbox.
The fact that it's not animating until you scroll in the scroll view is a symptom that your call to startAnimating is happening in a background thread. UIKit calls should be made in the main thread.
You can verify that it's happening in a background thread by adding code like this:
if ([NSThread isMainThread]) {
NSLog(#"Running on main thread.");
} else {
NSLog(#"Running on background thread.");
}
You'll want to make sure all of the code you showed in your question is running on the main thread. To do this, you can change your code to look something like this:
// this code would be wherever your existing code was
[self performSelectorOnMainThread:#selector(addActivityIndicatorToView:) withObject:mainScrollView waitUntilDone:YES];
// this would be a new method in the same class that your existing code is in
- (void) addActivityIndicatorToView:(UIView*) view {
UIActivityIndicatorView *loaderActivity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
loaderActivity.center = CGPointMake(87/2,y+56/2);
loaderActivity.tag=tag;
[view addSubview:loaderActivity];
[loaderActivity startAnimating];
[loaderActivity release];
}
activityIndicator = [[UIActivityIndicatorView alloc]
initWithFrame:CGRectMake(87/2,y+56/2);
[activityIndicator setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhite];
activityIndicator.tag=tag;
[mainScrollView addSubview:loaderActivity];
[activityIndicator startAnimating];
[activityIndicator release];
Had an issue where I made the startAnimating call in the viewDidLoad and didn't work. I moved the call in the viewWillAppear and it worked!
Try running the animator in a different thread.
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:#selector(showProgress) object:nil];
[thread start];
//do whatever your want here
//call this when you want it stop animating
[activityIndicator stopAnimating];
[thread release];
- (void)showProgress{
[activityIndicator startAnimating];
}

QLPreviewController's view

I just trying to get to QLPreviewController.view. Indeed, I want to catch a tap event on its view to show/hide toolbar etc. I am trying:
QLPreviewController* qlpc = [QLPreviewController new];
qlpc.delegate = self;
qlpc.dataSource = self;
qlpc.currentPreviewItemIndex=qlIndex;
[navigator pushViewController:qlpc animated:YES];
qlpc.title = [path lastPathComponent];
[qlpc setToolbarItems:[NSArray arrayWithObjects:self.dirBrowserButton,self.space, self.editButton, self.btnSend, nil] animated:YES];
UITapGestureRecognizer* gestTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showControls:)];
gestTap.cancelsTouchesInView=NO;
[qlpc.view addGestureRecognizer:[gestTap autorelease]];
[qlpc release];
And nothing happens
If I attach UITapRecognizer onto navigationController.view, it fires only if I touch toolbar/navbar. UISwipeGestureRecognizer works fine in that case.
I tried to attach a transparent overlay view and add gesture recognizers on it, but no luck.
Well, I saw some apps that implements such a feature so obviously it is possible, but how?
Sorry, I googled all day long and didn't find any solution. Please, help me.
With your solution, does the QLPreviewController's view still recieve touches? I've tried to do something similar (I'm stealing the view from QLPreviewController to use it) and it looks like my overlay view doesn't let anything pass trough to the view lying behind it.
I have been working on this problem today and the suggestion to override -(void)contentWasTappedInPreviewContentController:(id)item {} is close but when you do you mess with the preview controllers handling.
So I stopped overriding that method and instead created a RAC signal that fires whenever the method is called. This does not mess with the default behavior of QL. I am doing it in a subclass of the QLPreviewController but that shouldn't be necessary.
I have a property on my class:
#property RACSignal *contentTapped;
Then in my init method of my subclass of QLPreviewController:
_contentTapped = [self rac_signalForSelector:#selector(contentWasTappedInPreviewContentController:)];
Now in another class or even internally you can use the signal like this:
previewController.contentTapped subscribeNext:^(id x) {
// Put your handler here!
}];
Here is my solution (to use KVO), where I'm monitoring navigation bar status - and showing toolbar when needed (it seems that it hides toolbar by itself when tapped)
#define kNavigationBarKeyPath #"navigationBar.hidden"
static void * const NavigationBarKVOContext = (void*)&NavigationBarKVOContext;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setToolbarHidden:NO];
[self.navigationController addObserver:self forKeyPath:kNavigationBarKeyPath options:NSKeyValueObservingOptionPrior context:NavigationBarKVOContext];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController removeObserver:self forKeyPath:kNavigationBarKeyPath];
}
And
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ( context == NavigationBarKVOContext ) {
BOOL prior = [change[NSKeyValueChangeNotificationIsPriorKey] boolValue];
if ( prior && self.navigationController.toolbarHidden ) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController setToolbarHidden:NO animated:YES];
});
}
}
}
I found none of the answers here to work, but the one that did for me was to subclass QLPreviewController and override viewDidAppear as so:
- (void)viewDidAppear:(BOOL)animated
{
UITapGestureRecognizer *gestTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showControls:)];
gestTap.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:[gestTap autorelease]];
}
Ok, solution is very simple.
just added an overlay view onto keyWindow. Attached gesture recognizers onto overlay and it works.
QLPreviewController* qlpc = [QLPreviewController new];
qlpc.delegate = self;
qlpc.dataSource = self;
qlpc.currentPreviewItemIndex=qlIndex;
[navigator pushViewController:qlpc animated:YES];
qlpc.title = [path lastPathComponent];
UIView* overlay = [[[UIView alloc] initWithFrame:navigator.view.bounds] autorelease];
[[[UIApplication sharedApplication] keyWindow] addSubview:overlay];
[overlay setNeedsDisplay];
[qlpc setToolbarItems:[NSArray arrayWithObjects:self.dirBrowserButton,self.space, self.editButton, self.btnSend, nil] animated:YES];
UITapGestureRecognizer* gestTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(showControls:)];
gestTap.cancelsTouchesInView=NO;
[overlay addGestureRecognizer:[gestTap autorelease]];
[qlpc release];
Subclass QLPreviewController and then override
-(void)contentWasTappedInPreviewContentController:(id)item
{}
Thats its !

App crashes at [UIWebView webView:didReceiveTitle:forFrame:]

I am implementing a simple in-app browser. In my home view (UITableViewController), I have something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
WebViewController *webViewController = [[WebViewController alloc] init];
switch (indexPath.row) {
case 0:
webViewController.stringURL = #"http://www.google.com";
break;
case 1:
webViewController.stringURL = #"http://www.bing.com";
break;
default:
webViewController.stringURL = #"http://stackoverflow.com";
break;
}
[self.navigationController pushViewController:webViewController animated:YES];
[webViewController release];
}
The app crashed after I repetitively navigated back and forth between my home view and webViewControllera few times.
Inside WebViewController class, I have nothing but a [UIWebView *webView] and a [UIActivityIndicatorView *activityIndicator]. Both are with attributes nonatomic, retain. Here is the implementation.
#import "WebViewController.h"
#implementation WebViewController
#synthesize webView, activityIndicator, stringURL;
- (void)dealloc
{
[self.webView release];
self.webView.delegate = nil;
[self.activityIndicator release];
[super dealloc];
}
-(void)loadView {
UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = contentView;
CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
webFrame.origin.y = 0.0f;
self.webView = [[UIWebView alloc] initWithFrame:webFrame];
self.webView.backgroundColor = [UIColor blueColor];
self.webView.scalesPageToFit = YES;
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.webView.delegate = self;
[self.view addSubview: self.webView];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.stringURL]]];
self.activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.activityIndicator.frame = CGRectMake(0.0, 0.0, 30.0, 30.0);
self.activityIndicator.center = self.view.center;
[self.view addSubview: self.activityIndicator];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadView];
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
// starting the load, show the activity indicator in the status bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[activityIndicator startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// finished loading, hide the activity indicator in the status bar
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[activityIndicator stopAnimating];
}
#end
I just ran my app in Instruments using the Zombies template, which shows -[UIWebView webView:didReceiveTitle:forFrame:] is the Zombie call. But I still can’t figure out what is actually the problem.
(Please download trace if needed)
Any help is greatly appreciated!
[Update]:
As #7KV7 and #David pointed out, there is an obvious bug in my dealloc function. I should call self.webView.delegate=nil; first before I release self.webView. Sorry about that. Unfortunately, after I fix it, the app still crashes in the same way.
If I delete [webViewController release]; from the first code block, the crash actually is gone. But obviously, there will be memory leak.
First of all, remove that call to loadView in viewDidLoad. The framework will the call the method when it doesn't find a view provided in XIB file. Second, your loadView is filled with memory leaks. You are allocating, initializing and retaining an object every time the method is called. So you are taking ownership twice and releasing it only once in the dealloc.
The objects are not being properly deallocated. You should do something like alloc-init-autorelease to solve this. Next thing is the that every time the controller gets loaded, because of your call to loadView, you end up creating two web view objects and two requests. You lose reference to one of them as you reassign. Herein, lies the problem mentioned in the title. You aren't able to reset the delegate of a web view object that has your controller as a delegate. Imagine a request being completed soon after you leave. Here the message will go to a zombie object. This is a pretty good example for why you need to nil out your delegates.
- (void)dealloc
{
self.webView.delegate = nil;
[self.webView release];
[self.activityIndicator release];
[super dealloc];
}
Try this dealloc. You were releasing the webview and then setting the delegate as nil. You should first set the delegate as nil and then release it. Hope this solves the issue.
I think what's happening is that you are going back while the page is still loading so the controller gets deallocated and then the webview finishes loading.
Try calling [webView stopLoading] in your viewDidUnload method to make sure this isn't happening.
Don't know if it's the cause of your problem, but this is definitely wrong:
[self.webView release];
self.webView.delegate = nil;
You cannot (safely) refer to self.webView after you release it!
Instead of pushing webViewController,add its view to self.view .
Dont call [self loadView]; in viewDidLoad.

UITableView reloadData causing all animations to stop working

Alright this is a really weird one so I am going to layout what is happening and then give some code after. For my example I am going to use a static amount of views, 2.
The Basics
I have a UIPageControl with X many Subviews added. On each subviews viewDidLoad is an NSXMLParse to grab an XML feed. Once the feed is obtained, it's parsed and the table is reloaded using the parsed array. There is also a Settings button on each view. When the Settings button is pressed, UIModalTransitionStyleCoverVertical:Animated:YES is run and a UINavigationController slides up into view with full animation. Dismiss also shows animation sliding out back to the previous view. If you are in Settings, you can PushViews two levels deep (Slide In Animation).
The Problem
A random amount of the time, when the app is built and run (Not Resumed) when you tap the Settings button, the Animation does not occur. Everything is functional except for all Core Animations are removed. DismissModal simply swaps back to the previous screen. PushView in the NavigationController no longer has any animation, the next view simply appears.
If you quit the app (Kill Process) and relaunch it, it may work fine for a period of time but at some point when you tap the Settings button, it will lose all animations.
The Details
I started with Apples PageControl Application for the groundwork. It creates a dynamic amount of views based on user settings.
- (void)awakeFromNib
{
kNumberOfPages = 2;
// view controllers are created lazily
// in the meantime, load the array with placeholders which will be replaced on demand
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (int i = 0; i < kNumberOfPages; i++)
{
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = kNumberOfPages;
pageControl.currentPage = 0;
// pages are created on demand
// load the visible page
// load the page on either side to avoid flashes when the user starts scrolling
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];
}
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= kNumberOfPages)
return;
// replace the placeholder if necessary
SecondViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[SecondViewController alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
if (controller.view.superview == nil)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
As each view is generated, it runs an NSXMLParse in its viewDidLoad. Everything works fine up to this point. Both views are generated and you can swipe between them.
If you push the Settings Button
- (IBAction)settingsButtonPressed:(id)sender;
{
SettingsViewController *settingsViewController = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
navigationController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navigationController animated:YES];
[settingsViewController release];
[navigationController release];
}
At this point, SettingsViewController appears into view. However, sometimes it slides up with its proper animation. Other times it will simply appear and all further core animations are broken until the process is restarted.
I went through and checked all of NSXMLParse and have narrowed down the problem to one line. On each of my subviews, is a tableView, after the XML Parsing is done, I created an array with the results and ran [self.tableview reloadData]. If I comment out that line, the table obviously only loads blank but it doesn't have any issues with Animations.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSMutableArray *tableData = ARRAY_GENERATED_HERE;
[self.tableView reloadData];
}
My Testing
I will note from my tests, everything is fine if kNumberOfPages is set to 1 instead of 2. Only 1 view gets generated, the Animation glitch never occurs. Add a second view in, usually within opening Settings five times, it will glitch.
Still haven't come to a solution but it has to do with [tableView reloadData]. Any insight would be great.
Daniel pointed out something that makes sense.
My XML is fetched in the viewDidLoad using:
[NSThread detachNewThreadSelector:#selector(parseXMLFileAtURL:) toTarget:self withObject:path];
- (void)parseXMLFileAtURL:(NSString *)URL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
stories = [[NSMutableArray alloc] init];
//you must then convert the path to a proper NSURL or it won't work
NSURL *xmlURL = [NSURL URLWithString:URL];
// here, for some reason you have to use NSClassFromString when trying to alloc NSXMLParser, otherwise you will get an object not found error
// this may be necessary only for the toolchain
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[rssParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[rssParser setShouldProcessNamespaces:NO];
[rssParser setShouldReportNamespacePrefixes:NO];
[rssParser setShouldResolveExternalEntities:NO];
[rssParser parse];
[pool release];
}
From your comment, you said you are running the parser in a background thread...UIKit is not thread safe and i suspect that is whats causing your problems...try making the reloadData call on the main thread, you can use NSObjects performSelectorInMainThread to do this...
[self performSelectorOnMainThread:#selector(operationComplete) withObject:nil waitUntilDone:false];