I've been having alot of problems creating a timer for a iphone application, currently I have a label displaying my timer, but it is just displaying the variable i (2700) in the label, and not decreasing by one every second. Its really driving my head in as this is really my 1st attempt coding in obj C, and for the life of me I cannot get it to decrease in the label.
I was hoping one of the programs on this site would be able to readover my code (MainViewController.m), with particular reference to the timer (countDown and viewDidLoad) and help me fix my problem.
Cheers in advance.
#import "MainViewController.h"
#include <unistd.h>
#include <stdio.h>
int i = 2700;
#implementation MainViewController
#synthesize window;
#synthesize InformationAboutRSI, mainView;
- (IBAction) InformationAboutRSI {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.rsi.org.au/what-is-rsi.html"]];
}
-(void)countDown {
if (i > 0) {
i--;
countdownLabel.text = [NSString stringWithFormat:#"%i",i];
sleep(100);
}
}
- (void)viewDidLoad {
int countDown();
[countdownLabel setFont:[UIFont fontWithName:#"DBLCDTempBlack" size:128.0]];
countdownLabel.text = [NSString stringWithFormat:#"%i",i];
[super viewDidLoad];
}
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)showInfo:(id)sender {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[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;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[window release];
[super dealloc];
}
-(void)updateLabel {
countdownLabel.text = [NSString stringWithFormat:#"%i",i];
}
#end
When developing applications for most modern operating systems, you generally don't want to block the UI thread. This means that your code should do its work and get out of the way as quickly as possible, because while it blocks, the application can't do anything else (like respond to button presses). On a desktop application, blocking the UI thread for more than a couple of seconds will display an hourglass or pinwheel cursor.
With that in mind, you could design your countDown method to:
Decrement the counter
Update the label
Create an NSTimer (I would try scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:) in your initialization to ask the operating system to call countDown every X seconds.
You are calling your countdown method only once, i don't see a loop or timer that would repeat this action.
Have a look at the NSTimer reference.
or use a classic for loop
-(void)countDown
{
for (int j = i, j > 0, j--)
{
countdownLabel.text = [NSString stringWithFormat:#"%i",j];
sleep(100);
}
}
you could call this method after loading your view and initializing the textfield with [self countDown];
You need to return from your countDown method without sleeping and without setting the label more than once. The display will only update after you exit your code.
Then you need to find a way to call your countDown method again after a long enough delay and without sleeping anywhere else. (Hint: use NSTimer) Rinse and repeat.
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;
}
I have view controller with UIImageView . In view did load i want to set image on image view for particular time interval.After that image view should be cleared and application should switch to next screen. I have tried following code which is not working:
welcomeImage.image=[UIImage imageNamed:#"abc.png"];
sleep(5);
homeScreenController *controller=[[homeScreenController alloc]initWithNibName:#"homeScreenController" bundle:nil];
controller.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:controller animated:YES];
In this case it is sleeping before view did load and it is not going to the next screen also. So what is wrong with the code?
Use the below code/..
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(goToNextView) withObject:nil afterDelay:5.0];
}
- (void)goToNextView
{
homeScreenController *controller=[[homeScreenController alloc]initWithNibName:#"homeScreenController" bundle:nil];
controller.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:controller animated:YES];
}
The easiest solution is to fire a event after a time. I would recommend using performSelector.
- (void) hide {
//For example.
[self dismissModalViewController];
}
- (void) viewDidLoad {
//Setup my image.
[self performSelector:#selector(hide) withObject:nil afterDelay:3];
}
you can use NSTimer
something like this
- (void) viewDidLoad {
.......
NSTimer *timer = [[NSTimer scheduledTimerWithTimeInterval:(2.5)
target:self selector:#selector(hideImage)
userInfo:nil repeats:NO] retain];
....
}
- (void)hideImage
{
yourImage.hidden = YES;
}
Also, you made a sleep(5) inside the Main Thread, that's bad.
try something like
[self performSelectorOnBackground:#selector(hide) withObject:nil]
and do the thing you want (and the sleep(5)) inside the method -(void)hide like
-(void) hide {
sleep(5)
// ...
}
Good luck ;)
My iPhone app badly leaks when flipping back and forth between a main uiviewcontroller and a help uiviewcontroller .
Here is the source of the main view, followed by source of the help view.
MAIN VIEW - FLIP TO HELP.....................
// Changes from operational view to Help view.
- (IBAction)showHelp:(id)sender
{
// End trial mode:
self.stop_trial_if_started;
self.rename_trial_if_edited;
// Switch to trial help:
help_view_context = 0;
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
HELP VIEW - INIT.............................
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
help_scroll.editable = FALSE;
return;
}
HELP - RETURN TO MAIN VIEW.........................
// User clicked the button to return to operational view:
- (IBAction)done:(id)sender {
NSLog(#"help- done");
if( help_view_context == 0 ) {
[self.delegate trial_help_DidFinish:self];
}else{
[self.delegate file_help_DidFinish:self];
}
}
MAIN VIEW - RETURN FROM HELP...............................
// Inits operational view when user changes from Help view back to operational view.
- (void)trial_help_DidFinish:(HelpView *)controller {
NSLog(#"trial_help_DidFinish");
[self dismissModalViewControllerAnimated:YES];
self.init_trial_operation;
}
You are creating a controller with ref count of 1 and a local reference each time showHelp: is called:
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
you are losing your reference to it at the end of this method.
You happen to have references to it in done: (self) and *_help_didFinish (controller), but you never release it in either of those locations. Dismissing the controller is fine, but you also have to release it.
(Another option would be to never create a second one, and maintain an iVar to the original.)
You could well be leaking on this line
controller.delegate = self;
What is your property declaration for the delegate. If it's anything other than assign, then you either need to change it (preferred option) or make sure you are releasing it in the dealloc method of HelpView controller.
Using the PhotoScroller example from Apple, to reuse the memory allocated for views, I cannot get the memory released once the retain count hits 0. Here my code for a better understanding:
This piece is an extract from PhotoScroller
PhotoViewController.m
- (void)configurePage:(ImageScrollView *)page forIndex:(NSUInteger)index
{
page.index = index;
page.frame = [self frameForPageAtIndex:index];
[page displayPage:index];
}
ImageScrollView.h
#interface ImageScrollView : UIView
{
UIViewController *vc;
NSUInteger index;
}
#property (assign) NSUInteger index;
- (void)displayPage:(int)indice;
#end
ImageScrollView.m
- (void)dealloc
{
[vc release];
[super dealloc];
}
- (void)displayPage:(int)indice
{
//remove previous view
[vc.view removeFromSuperview];
[vc release];
vc = nil;
//NSLog(#"vc retain %i", [vc retainCount]);
// make a new viewController for the new page
Class clss = NSClassFromString([NSString stringWithFormat:#"page_0%i", indice + 1]);
vc = [[clss alloc] initWithNibName:[NSString stringWithFormat:#"page_0%i", indice + 1] bundle:nil];
[self addSubview:vc.view];
}
The classes "page_0x" are UIViewControllers. So far have nothing but a UIImageView on the XIB.
An example of page_01:
#implementation page_01
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Memory peaks to 148 MB on device. Gives me a memory warning Level 1, then releases some memory. Goes down to 95 MB. Keeps going up and down.
Perhaps your page_0x view controllers are getting freed, but something they've created isn't. Some things to check:
Set a breakpoint in the dealloc method of your page_0x view controllers. Is it getting called?
Check all the IBOutlets and other instance variables of your page_0x view controllers. Are they all being released properly in their class's dealloc method?
Run Build & Analyze. Does it turn up anything?
Try running the Leaks Instrument. It can tell you what kind of objects are actually leaking.
(EDIT) Grasping at straws now. You don't have NSZombieEnabled turned on, do you?
(EDIT 2) You say you're throwing a .png on it. What happens if you remove that .png?
If you're running in the simulator, try it on the device. (See this question.)
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.