Recently I have come across a problem, where my app goes into background during initial launch, but this only happens on iPhone 4S with iOS6. I have tested on:
simulator, with different hardware/software configurations
iPhone 5 (iOS 6)
iPhone 4S (iOS 5.1)
iPad2 (iOS 6)
and it is working on all of them, but on the iPhone 4S with iOS6 the launch of the app takes about 20s before going into background, if you "re-launch" the app after a couple of seconds you see that it is still running and works without any problem.
Is there any know issue with iPhone4S (iOS6) that causes this or there is something special about this model? [I already tested it on different iPhone4s (iOS6) and it is happening on all of them]
EDIT
I noticed something weird will doing some more testing, iPhone 4s (iOS6) is the only one that doesn't show the loading screen (first view controller), it only shows the launch image.. anyway, here is the code for the AppDelegate and the first view controller:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Optional: automatically track uncaught exceptions with Google Analytics.
[GAI sharedInstance].trackUncaughtExceptions = YES;
// Optional: set Google Analytics dispatch interval to e.g. 20 seconds.
[GAI sharedInstance].dispatchInterval = 20;
// Optional: set debug to YES for extra debugging information.
[GAI sharedInstance].debug = NO;
// Create tracker instance.
__unused id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:#"UA-APP-ID"];
return YES;
}
EcraPrincipalViewController.m
#import "EcraPrincipalViewController.h"
#import "ListaPercursosTableViewController.h"
#import "GlobalVars.h"
#import "AppDelegate.h"
#interface EcraPrincipalViewController ()
#end
#implementation EcraPrincipalViewController
{
int progresso;
int sizePercursos;
}
#synthesize lbl_versao;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.trackedViewName = #"iPhone - EcrãPrincipal";
// Do any additional setup after loading the view.
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
lbl_info.text = NSLocalizedString(#"downloading", NULL);
lbl_versao.text = NSLocalizedString(#"walkme_versao", NULL);
dispatch_queue_t queue = dispatch_queue_create("com.WalkMe.downloadPercursos", NULL);
dispatch_async(queue, ^{
[[GlobalVars Instance] getLevadas:self];
});
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (BOOL)shouldAutorotate
{
return [[GlobalVars Instance] podeRodar];
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)novaAtualizacao
{
NSLog(#"Atualização encontrada");
lbl_info.text = NSLocalizedString(#"atualizacao_encontrada", NULL);
}
-(void)atualizarPercursosInternos
{
NSLog(#"VERIFICAÇAO Interna");
lbl_info.text = NSLocalizedString(#"a_atualizar_percursos", NULL);
}
-(void)setNewDownload:(NSNumber*)size
{
NSLog(#"Iniciou VERIFICAÇAO");
progresso = 0;
sizePercursos = [size intValue];
lbl_info.text = [NSString stringWithFormat:#"%# 0/%d", NSLocalizedString(#"novos_percursos", NULL), sizePercursos];
}
-(void)setProgress
{
NSLog(#"Progresso");
progresso++;
lbl_info.text = [NSString stringWithFormat:#"%# %d/%d", NSLocalizedString(#"novos_percursos", NULL), progresso, sizePercursos];
}
-(void)goToLista
{
NSLog(#"ACABOU VERIFICAÇAO");
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
[[GlobalVars Instance] getLevadas];
[self.navigationController pushViewController:[story instantiateViewControllerWithIdentifier:#"view_lista"] animated:NO];
}
- (void)viewDidUnload {
[self setLbl_versao:nil];
[super viewDidUnload];
}
#end
Thank you very much for your attention and help :)
While trying to figure out what was happening, the suggestions made in the comments to the question made me realize that for some reason the app was calling the same method twice and for some reason that cause the app to go to the background.
To solve the problem, simply remove this lines from viewDidLoad
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
and add them to goToLista (this is called after the initial loading is complete).
I ended up with:
- (void)viewDidLoad
{
[super viewDidLoad];
self.trackedViewName = #"iPhone - EcrãPrincipal";
// Do any additional setup after loading the view.
lbl_info.text = NSLocalizedString(#"downloading", NULL);
lbl_versao.text = NSLocalizedString(#"walkme_versao", NULL);
dispatch_queue_t queue = dispatch_queue_create("com.WalkMe.downloadPercursos", NULL);
dispatch_async(queue, ^{
[[GlobalVars Instance] getLevadas:self];
});
}
-(void)goToLista
{
NSLog(#"ACABOU VERIFICAÇAO");
[[GlobalVars Instance] getLevadas];
NSMutableArray *views = [[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
UIStoryboard *story = [UIStoryboard storyboardWithName:#"WalkMeStoryBoard" bundle:nil];
ListaPercursosTableViewController *listaV = [story instantiateViewControllerWithIdentifier:#"view_lista"];
[views replaceObjectAtIndex:0 withObject:listaV];
[self.navigationController setViewControllers:views];
[self.navigationController pushViewController:[story instantiateViewControllerWithIdentifier:#"view_lista"] animated:NO];
}
This solves the problem, but I don't know why. If anyone knows please do tell, because I really would like to know.
Thank you :)
Related
This question is very similar to an existing question asked here UIImagePickerControllerCameraDeviceFront only works every other time I tried the solution presented but it didn't work for me
I have a simplest of a project with two view controllers. In the blue one I am displaying a small UIView with a UIImagePickerController in it. NOTE: I am displaying front facing camera when app is launched.
I hit the next button and go to orange view controller and when I hit the back button and come back to blue view controller the UIImagePickerController flips from Front to rear. I guess the reason is that it thinks its busy and moves to the rear cam. If I keep moving back and forth between the view controllers the camera keeps flipping front, back, front, back, front, back...
Here is my code and screenshots, what am I doing wrong?
In my *.h
#import <UIKit/UIKit.h>
#interface v1ViewController : UIViewController <UIImagePickerControllerDelegate>
{
UIImagePickerController *picpicker;
UIView *controllerView;
}
#property (nonatomic, retain) UIImagePickerController *picpicker;
#property (nonatomic, retain) UIView *controllerView;
#end
In my *.m file (This code is only used when blue colored view controller is displayed)
#import "v1ViewController.h"
#import <MobileCoreServices/UTCoreTypes.h>
#implementation v1ViewController
#synthesize picpicker;
#synthesize controllerView;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
controllerView = picpicker.view;
[controllerView setFrame:CGRectMake(35, 31, 250, 250)];
controllerView.alpha = 0.0;
controllerView.transform = CGAffineTransformMakeScale(1.0, 1.0);
[self.view addSubview:controllerView];
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
controllerView.alpha = 1.0;
}
completion:nil
];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[picpicker dismissModalViewControllerAnimated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
} else {
return YES;
}
}
#end
You are dismissing the controller in both the viewDidDisappear and viewWillDisappear methods.
That could be the cause of your problem.
Although I do not have a device with a camera available right now to verify this, it seems that you're not dismissing the pickerview controller correctly. The documentation states that you should call dismissModalViewControllerAnimated: on the parent controller in order to dismiss the picker (though, calls to presented controllers will propagate to presenters - so this is not the problem), but in your case you're not displaying the controller modally in the first place so it will not work.
What I would try in this case is to release the picker instead (if not under ARC) and set it to nil (instead of calling [picpicker dismissModalViewControllerAnimated:YES];).
PS. In fact, it seems that there is a bigger problem with your design. Since each button is set to present the other party modally you are not dismissing any of the controllers ever. The controllers just keep stacking on each other. You should either consider to embed them in a navigation controller and have it handle the hierarchy or just set dismissModalViewControllerAnimated: (dismissViewControllerAnimated:completion: on iOS5+) as the action of the second controller's button instead of a modal segue.
This is a very simple issue. I don't know why this happens exactly, but it seems that UIImagePickerController was designed to recreated each time it's needed instead of keeping any reference to it, which seems logical if you think about it. Basically, you need to recreate and reconfigure your picker each time. Below I've pasted some code to give an image of what I mean.
Simple solution:
- (UIImagePickerController *)loadImagePicker {
UIImagePickerController *picpicker = [[UIImagePickerController alloc] init];
picpicker.delegate = self;
picpicker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeImage, nil];
picpicker.sourceType = UIImagePickerControllerSourceTypeCamera;
picpicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picpicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
picpicker.showsCameraControls = NO;
picpicker.navigationBarHidden = NO;
picpicker.wantsFullScreenLayout = NO;
return picpicker;
}
and in:
-(void)viewWillAppear:(BOOL)animated{
if(!self.picpicker){
self.picpicker = [self loadImagePicker];
[self.view addSubview: self.picpicker];
}
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.picpicker removeFromSuperview];
self.picpicker = nil;
}
So i need to write a QR reader for an iphone application. Usually frameworks like ZXING and ZBAr allow you to download an application and then read the barcode. I want to integrate it within the application itself. So basically i want to be able to tap a button and have it reading the QR code. Is this possible? If so are there any proper documentation on this? I tried using ZXing but when i link binaries libZXingWidget.a is unavailable. Also the documentation is not enough to know how to integrate it within the application. So let me know.
Here's code to setup ZBar, make sure to add the SDK to your project and link your library.
-
(void)viewDidLoad
{
[ZBarReaderView class];
readerView.readerDelegate = self;
readerView.tracksSymbols = NO;
//CHOOSE CAMERA
if (some setting isEqual to CameraRear) {
readerView.device = [self backFacingCameraIfAvailable];
}
else {
readerView.device = [self frontFacingCameraIfAvailable];
}
[self relocateReaderPopover:[self interfaceOrientation]];
[readerView start];
}
-(AVCaptureDevice *)frontFacingCameraIfAvailable
{
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *captureDevice = nil;
for (AVCaptureDevice *device in videoDevices)
{
if (device.position == AVCaptureDevicePositionFront)
{
captureDevice = device;
break;
}
}
// couldn't find one on the front, so just get the default video device.
if ( ! captureDevice)
{
captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return captureDevice;
}
-(AVCaptureDevice *)backFacingCameraIfAvailable
{
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *captureDevice = nil;
for (AVCaptureDevice *device in videoDevices)
{
if (device.position == AVCaptureDevicePositionBack)
{
captureDevice = device;
break;
}
}
// couldn't find one on the front, so just get the default video device.
if ( ! captureDevice)
{
captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return captureDevice;
}
-(void)relocateReaderPopover:(UIInterfaceOrientation)toInterfaceOrientation{
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
readerView.previewTransform = CGAffineTransformMakeRotation(M_PI_2);
} else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
readerView.previewTransform = CGAffineTransformMakeRotation(-M_PI_2);
} else if (toInterfaceOrientation== UIInterfaceOrientationPortraitUpsideDown) {
readerView.previewTransform = CGAffineTransformMakeRotation(M_PI);
} else {
readerView.previewTransform = CGAffineTransformIdentity;
}
}
This is a step-by-step tutorial on how to add an integrated QR code reader to your iphone app. Look at this example ZXing project if you need to know how to use it.
I have managed to integrate ZXING into my test project. I have done this a little time ago so I might not remember all the problems that I faced and solved.
Into my workspace folder I have copied ZXing-2.0 folder. Inside I have only left folders: cpp, docs and iphone. To my workspace (containing the test project) I have added ZXingWidget project from the folder ZXing-2.0. This allowed me to add to linking setting libZXingWidget.a.
When I build my test project, XCode detects dependency and builds the widget first and then builds the test project and links it against libZXingWidget.a.
Here's a simple view controller that I have implemented to display inside camera view able to detect QR code.
//
// MyVC.m
//
//
#import "MyVC.h"
#import "QRCodeReader.h"
#interface MyVC () {
ZXingWidgetController *_widController;
}
#end
#implementation MyVC
#synthesize labelResultString;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
_widController = [[ZXingWidgetController alloc] initWithDelegate:(id<ZXingDelegate>)self showCancel:YES OneDMode:NO];
QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
NSSet *readers = [[NSSet alloc ] initWithObjects:qrcodeReader,nil];
//[qrcodeReader release];
_widController.readers = readers;
//[readers release];
//NSBundle *mainBundle = [NSBundle mainBundle];
//_widController.soundToPlay = [NSURL fileURLWithPath:[mainBundle pathForResource:#"beep-beep" ofType:#"aiff"] isDirectory:NO];
_widController.overlayView.displayedMessage = #"";
_widController.wantsFullScreenLayout = NO;
//[self presentModalViewController:_widController animated:NO];
_widController.view.frame = CGRectMake(10, 10, 300, 300);//self.view.frame;
_widController.view.autoresizingMask = UIViewAutoresizingNone;
_widController.overlayView.frame = CGRectMake(0, 0, 300, 300);
_widController.overlayView.cropRect = CGRectMake(20, 20, 260, 260);
[self.view addSubview:_widController.view];
//[_widController release];
}
- (void)viewDidUnload {
[super viewDidUnload];
self.labelResultString = nil;
}
- (void)dealloc {
self.labelResultString = nil;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[_widController viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[_widController viewDidAppear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_widController viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_widController viewDidDisappear:animated];
}
#pragma mark - ZXingDelegateMethods
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result {
self.labelResultString.text = result;
}
- (void)zxingControllerDidCancel:(ZXingWidgetController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
#end
Once you have ZXing set up correctly the code to get a QR reader is literally this easy:
Make sure to import these two guys for QR:
#import "ZXingWidgetController.h"
#import "QRCodeReader.h"
Then in your controller you will set up the code reader as follows:
ZXingWidgetController *widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
QRCodeReader *qrcodeReader = [[QRCodeReader alloc] init];
NSSet *readers = [[NSSet alloc] initWithObjects:qrcodeReader,nil];
widController.readers = readers;
[self presentModalViewController:widController animated:YES];
You basically set up the controller that handles the code reading (ZXingWidgetController) and then give it a set of all the types of code readers you want (here I just used QRCodeReader.) Lastly, you just present it as a modal view controller.
Then you will need to implement the <ZXingDelegate> and the following delegate functions:
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result
{
[self dismissModalViewControllerAnimated:YES];
NSLog(#"%#",result); //Simple NSString result.
}
- (void)zxingControllerDidCancel:(ZXingWidgetController*)controller
{
[self dismissModalViewControllerAnimated:YES];
NSLog(#"Cancelled");
}
The hardest part is just setting it up. I found this tutorial to be the most helpful (Sorry that I am using a link): How to install ZXing in Xcode 4 The comments are also helpful if you have any problems.
Update
I swapped out completely different code for pushViewController, and it is still crashing... seems like pushViewController is not the culprit. Here is what I added instead:
NSString *videoURL = [[NSString alloc] initWithFormat:#"http://www.vimeo.com/m/#/%#", videoID];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:videoURL]];
It opens up the URL in Safari, and then crashes.. wtf?
PushViewController crashes with no error in the console, but I do get an EXC_BAD_ACCESS error in Xcode. The crash doesn't happen until after the view controller has been pushed... but the view its pushing is empty... no code to mess up.
My code is below:
MainViewController.m
PlayVimeo *playTest = [[PlayVimeo alloc] initWithNibName:#"PlayVimeo" bundle:nil];
//playTest.videoID = videoID;
[self.navigationController pushViewController:playTest animated:YES];
[playTest release];
PlayVimeo.m
#import "PlayVimeo.h"
#import "SVProgressHUD.h"
#implementation PlayVimeo
#synthesize videoID, wView;
-(void)viewDidLoad {
[super viewDidLoad];
//Show loading alert
[SVProgressHUD showInView:self.view status:#"Loading Video..."];
}
-(void)viewWillAppear:(BOOL)animated {
NSLog(#"Play View Loaded!");
[self vimeoVideo];
}
-(void)vimeoVideo {
NSLog(#"Video ID: %#", videoID);
NSString *html = [NSString stringWithFormat:#"<html>"
#"<head>"
#"<meta name = \"viewport\" content =\"initial-scale = 1.0, user-scalable = no, width = 460\"/></head>"
#"<frameset border=\"0\">"
#"<frame src=\"http://player.vimeo.com/video/%#?title=0&byline=0&portrait=1&autoplay=1\" width=\"460\" height=\"320\" frameborder=\"0\"></frame>"
#"</frameset>"
#"</html>",
videoID];
NSLog(#"HTML String: %#", html);
[wView loadHTMLString:html baseURL:[NSURL URLWithString:#""]];
//Dismiss loading alert
[SVProgressHUD dismissWithSuccess:#"Playing..."];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
-(void)dealloc {
[super dealloc];
}
Navigation Controller Code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
[Appirater appLaunched];
return YES;
}
Console on crash:
sharedlibrary apply-load-rules all
Current language: auto; currently objective-c
(gdb)
It's likely that the culprit is
[playTest release];
Without seeing the rest of your code, I would still say that you likely need to release this after you're done with the video.
The code can not be fixed, it seems. With the UIWebView class reference, there is an example program TransWeb. Take this as base, it has a window and a navigation controller with a webview in it (in the xib). In MyViewController it reads a html-file and displays it. What you need to do is to change the main view to landscape and replace the html-code with yours. Avoid the frame-stuff.
I have searched endlessly to no avail on this, hopefully someone can help!
I have a UIScrollView on the left half of a UIView which loads in custom cells/subviews. The UIView is part of a UINavigation stack, and it's also loaded into a tab on a TabBar.
What happens is, if I start the app and begin scrolling right away it's very smooth. However if I start the app and wait 5-10 seconds, the UIScrollView is VERY laggy and choppy (and it stays like that). I would think that it would be a memory leak or something, but I can't seem to find anything.
Included is the code of the view where I'm loading the custom cells/subviews into the UIScrollView. There isn't any custom code in the cell subview. Oh, and there's only about 8-10 items: each item has a small (150x150) image, and 3 text fields - all opaque.
#import "ProductListViewController.h"
#import "ProductListLeftItemViewController.h"
#implementation ProductListViewController
#synthesize listScroll;
- (void)viewDidLoad {
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:#"Products" ofType:#"plist"];
NSArray *products = [[NSArray alloc] initWithContentsOfFile:path];
//
int numProducts;
numProducts = [products count];
[listScroll setContentSize:CGSizeMake(500, (numProducts * 111))];
for (int i = 0; i < numProducts; i++) {
ProductListLeftItemViewController *cellItem = [[ProductListLeftItemViewController alloc] initWithNibName:#"ProductListLeftItem" bundle:nil];
cellItem.view.frame = CGRectMake(0, (i*111), 500, 111);
[self.listScroll addSubview:cellItem.view];
cellItem = nil;
[cellItem release];
}
[products release];
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
NSLog(#"Memory warning, ProductListViewController");
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[listScroll release];
[super dealloc];
}
#end
And as requested, the ProductListLeftItemViewController code:
#import "ProductListLeftItemViewController.h"
#implementation ProductListLeftItemViewController
#synthesize titleTxt, descriptionTxt, modelTxt, productThumb;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
titleTxt = nil;
descriptionTxt = nil;
modelTxt = nil;
productThumb = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[titleTxt release];
[descriptionTxt release];
[modelTxt release];
[productThumb release];
[super dealloc];
}
#end
For anyone that has this problem in the future, once I started testing things on the actual device, everything worked perfectly smooth. Maybe it was just something quirky with the simulator? I'll consider this resolved, even though I'm still going to look into it a little further.
I have a UITabbBarController with a UITableView. Under certain circumstances the TabBarControllers dataset requires updating when a user arrives from another view,
e.g. the initial load when the TabBarController is called the first time, or when the settings are changed.
This dataset update takes about 2 seconds and I want to show an UIActivityIndicatorView.
Trouble is that when I enter from another view I don't know which view to attach it to, since the loading of the tabbarController is carried out in the viewWillAppear method.
Any clues how I can go about this?
I've done this sort of thing in the viewDidAppear method. My code kicks off a background task to load the data from a url. It also hands the background task a selector of a method to call on the controller when it is done. That way the controller is notified that the data has been downloaded and can refresh.
I don't know if this is the best way to do this, but so far it's working fine for me :-)
To give some more details, in addition to the selector of the method to call when the background task has loaded the data, I also and it a selector of a method on the controller which does the loading. That way the background task manages whats going on, but the view controller provides the data specific code.
Here's there viewDidAppear code:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (reloadData) {
BackgroundTask *task = [[BackgroundTask alloc] initWithMethod:#selector(loadData) onObject:self];
task.superView = self.view.superview;
task.notifyWhenFinishedMethod = #selector(loadFinished);
[task start];
[task release];
}
}
The background task has an optional superView because it will add a new UIView to it containing an activity indicator.
BackgroundTask.m looks like this:
#implementation BackgroundTask
#synthesize superView;
#synthesize longRunningMethod;
#synthesize notifyWhenFinishedMethod;
#synthesize obj;
- (BackgroundTask *) initWithMethod:(SEL)aLongRunningMethod onObject:(id)aObj {
self = [super init];
if (self != nil) {
self.longRunningMethod = aLongRunningMethod;
self.obj = aObj;
}
return self;
}
- (void) start {
// Fire into the background.
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(execute:)object:nil];
thread.name = #"BackgroundTask thread";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(taskFinished:) name:NSThreadWillExitNotification object:thread];
[thread start];
[thread release];
}
- (void) execute:(id)anObject {
// New thread = new pool.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (self.superView != nil) {
busyIndicatorView = [[BusyIndicator alloc] initWithSuperview:self.superView];
[busyIndicatorView performSelectorOnMainThread:#selector(addToSuperView)withObject:nil waitUntilDone:YES];
}
// Do the work on this thread.
[self.obj performSelector:self.longRunningMethod];
if (self.superView != nil) {
[busyIndicatorView performSelectorOnMainThread:#selector(removeFromSuperView)withObject:nil waitUntilDone:YES];
}
[pool release];
}
- (void) taskFinished:(NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSThreadWillExitNotification object:notification.object];
[self performSelectorOnMainThread:#selector(notifyObject)withObject:nil waitUntilDone:NO];
}
- (void) notifyObject {
// Tell the main thread we are done.
if (self.notifyWhenFinishedMethod != nil) {
[self.obj performSelectorOnMainThread:self.notifyWhenFinishedMethod withObject:nil waitUntilDone:NO];
}
}
- (void) dealloc {
self.notifyWhenFinishedMethod = nil;
self.superView = nil;
self.longRunningMethod = nil;
self.obj = nil;
[super dealloc];
}
#end
Finally as I said I put up a activity indicator. I have a xib which contains a 50% transparent blue background with an activity indicator in the middle. There is a controller for it which has this code:
#implementation BusyIndicator
#synthesize superView;
#synthesize busy;
- (BusyIndicator *) initWithSuperview:(UIView *)aSuperView {
self = [super initWithNibName:#"BusyIndicator" bundle:nil];
if (self != nil) {
self.superView = aSuperView;
}
return self;
}
- (void) addToSuperView {
// Adjust view size to match the superview.
[self.superView addSubview:self.view];
self.view.frame = CGRectMake(0,0, self.superView.frame.size.width, self.superView.frame.size.height);
//Set position of the indicator to the middle of the screen.
int top = (int)(self.view.frame.size.height - self.busy.frame.size.height) / 2;
self.busy.frame = CGRectMake(self.busy.frame.origin.x, top, self.busy.frame.size.width, self.busy.frame.size.height);
[self.busy startAnimating];
}
- (void) removeFromSuperView {
[self.busy stopAnimating];
[self.view removeFromSuperview];
}
- (void) dealloc {
self.superView = nil;
[super dealloc];
}
#end
Hoep this helps.