I have an issue. Maybe I've got something in the wrong place or something, but dang I just can't figure it out! Any help would be appreciated.
I am trying to simply make a clock programmatically that shows only in my subview.
I've setup a timer along with an updater -(void) that I want to show in my subView that I create. I am building the subview programmatically which is the reason for not adding an IBOutlet and just connecting it in my storyboard - I'm trying do do it all with just code.
I am getting the error on my updateTimer label that says - Use of undeclared identifier 'rightLabel' and it's just not working out for me. HA HA - any help would be GREATLY appreciated!
.h
#interface DemoRootViewController : UIViewController <PaperFoldViewDelegate> {
NSTimer *timer;
}
#property (nonatomic, strong) UIView *rightView;
-(void)updateTimer;
.m
- (id)init
{
self = [super init];
if (self) {
//Timer Setup
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
//PaperFold Setup
_paperFoldView = [[PaperFoldView alloc] initWithFrame:CGRectMake(0,0,[self.view bounds].size.width,[self.view bounds].size.height)];
[_paperFoldView setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth];
[self.view addSubview:_paperFoldView];
//Setup Subview
_rightView = [[UIView alloc] initWithFrame:CGRectMake(0,0,240,[self.view bounds].size.height)];
//Setup UILabel
UILabel *rightLabel = [[UILabel alloc] initWithFrame:_rightView.frame];
[_rightView addSubview:rightLabel];
//Using PaperFold Framework to Add the Subview (this works fine)
[_paperFoldView setRightFoldContentView:_rightView foldCount:2 pullFactor:1];
}
return self;
}
-(void)updateTimer {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm:ss"];
//This is where I get my error "Use of Undeclared Identifier 'rightLabel'
rightLabel.text = [formatter stringFromDate:[NSDate date]];
}
#end
You don't have rightLabel declared as a property (or instance variable). Just change your .m to the code below:
.m
#interface DemoRootViewController ()
#property (nonatomic, strong) UILabel *rightLabel;
#property (nonatomic, strong) NSDateFormatter *dateFormatter;
#end
#implementation DemoRootViewController
- (id)init
{
self = [super init];
if (self) {
//Timer Setup
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateTimer) userInfo:nil repeats:YES];
//PaperFold Setup
_paperFoldView = [[PaperFoldView alloc] initWithFrame:CGRectMake(0,0,[self.view bounds].size.width,[self.view bounds].size.height)];
[_paperFoldView setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth];
[self.view addSubview:_paperFoldView];
//Setup Subview
_rightView = [[UIView alloc] initWithFrame:CGRectMake(0,0,240,[self.view bounds].size.height)];
//Setup UILabel
self.rightLabel = [[UILabel alloc] initWithFrame:_rightView.frame];
[_rightView addSubview:self.rightLabel];
//Using PaperFold Framework to Add the Subview (this works fine)
[_paperFoldView setRightFoldContentView:_rightView foldCount:2 pullFactor:1];
//Formatter setup
self.formatter = [[NSDateFormatter alloc] init];
[self.formatter setDateFormat:#"hh:mm:ss"];
}
return self;
}
-(void)updateTimer {
self.rightLabel.text = [self.formatter stringFromDate:[NSDate date]];
}
#end
The #interface DemoRootViewController () part is called a class extension. It essentially allows you "private" properties, so they are not exposed through the header file. If you want them exposed, simply put the two property definitions into the .h file.
Since you want to update the label, make the label variable an instance variable. Then you can create it in the init method and access it in the updateTimer method.
Also, make the date formatter an instance variable so you only need to create it once in the init method. No need to create a new date formatter every half second.
And since your timer should be a private instance variable, remove it from the .h file and put in the .m file.
#implementation DemoRootViewController {
NSTimer *timer;
}
Add the rightLabel and formatter ivars there too.
Also, remove the declaration of updateTimer from the .h file. This is a private method only used by the implementation in the .m file. Adding it to the .h file allows other classes to call the method.
You have to declare UILabel *rightLabel in .h file because you are using rightLabel in another method also.
Related
I'm new to developing iOS apps and English is not my native language, so please excuse any mistakes and my ugly code.
The app I'm trying to create should just display one specific image at one specific day (and change the image if the date changes).
Therefore I implemented an infinite loop in which the date is checked. If it differs from the one the image was changed the last time, the image changes again. The images are named in a "YearMonthDay.png"-scheme (e.g. "20131017.png").
I already googled a lot and got some code together (I know it's pretty ugly), but it crashes everytime.
I would really appreciate any help!
smViewController.h:
#import <UIKit/UIKit.h>
#interface smViewController : UIViewController {
UIImageView* mImageView;
}
#property (nonatomic, retain) IBOutlet UIImageView* imageView;
- (IBAction)contentModeChanged:(UISegmentedControl*)segmentedControl;
#end
smViewController.m
#import "smViewController.h"
#interface smViewController ()
#end
#implementation smViewController
#synthesize imageView = mImageView;
- (void)viewDidUnload
{
self.imageView = nil;
[super viewDidUnload];
}
- (void)dealloc
{
[mImageView release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *oldDateString = #"";
while(true)
{
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy/MM/dd"];
NSString *dateString = [dateFormat stringFromDate:today];
NSLog(#"date: %#", dateString);
if([dateString isEqualToString: oldDateString])
{
}
else
{
NSAssert(self.imageView, #"self.imageView is nil. Check your IBOutlet connections");
UIImage* image = [UIImage imageNamed:dateString];
NSAssert(image, #"image is nil. Check that you added the image to your bundle and that the filename above matches the name of you image.");
self.imageView.backgroundColor = [UIColor whiteColor];
self.imageView.clipsToBounds = YES;
self.imageView.image = image;
oldDateString = dateString;
}
[dateFormat release];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You should override - (void)applicationSignificantTimeChange:(UIApplication *)application in your apps UIApplicationDelegate. Then you will receive a event when date changes and you can remove any loop or timer.
Is probably because of the while loop. It's blocking your app.
You should use timers instead, something like this:
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(checkDate) userInfo:nil repeats:YES];
[timer fire];
Where you want to check for a new date every 1 seconds (you probably would be fine with higher frequency) and checkDate is the method where you check the date and replace the image if needed.
I have been having this issue from last couple of hours and did all the search that i could but unfortunately, i didnt find anything that resolves my issue....
Scenario: i have a CountDownTimer in TimerViewController, NSTimer and other methods are set up in AppDelegate which is suppose to update TimerViewController's Label... as per label's setter, i'm getting the value correctly and its showing in the NSLog HOWEVER, the label is not updating on the screen... this setter is being called from AppDelegate every second and the Label is suppose to show the Timer,
- (void)setMainTimerLabel:(UILabel *)mainTimerLabel
{
_mainTimerLabel = mainTimerLabel;
NSLog(#"ValueUpdated %#",_mainTimerLabel);
}
I have double checked the label, it hooked up with interface correctly, i tried to update the label from ViewDidLoad with test String, the label was showing me the string...
Help please!
EDIT:
AppDelegate Code:
AppDelegate.h
#property (nonatomic, strong) TimerViewController *TimerVC;
- (void)fireTimer;
AppDelegate.m
- (void)fireTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countDownTimer) userInfo:nil repeats:YES];
}
- (void) countDownTimer
{
.......
TimerVC = [[TimerViewController alloc]init];
self.TimerVC.mainTimerLabel = [NSString stringWithFormat:#"%02d:%02d:%02d",hours,minutes,seconds];
.......
}
I resolved this issue following the below code by jabobadilla
I actually solved it by performing a method that will go and retrieve the value that the NSTimer is updating in my AppDelegate, since the method firing the NSTimer is no longer in the main thread when I leave the view and come back to it. This method will loop as long as my NSTimer is valid. I also placed a delay, allowing for the UI to update the value, and then perform the method again. Here is the code in case it helps someone running into a similar issue. I got this idea from the suggestion provided by chandan, thanks!!
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
}
#property (nonatomic, retain) NSTimer *countdownTimer;
#property (nonatomic, retain) NSString *timeString;
CountdownTimerViewController.h
#interface CountdownTimerViewController : UIViewController {
AppDelegate *appdelegate;
}
#property (strong, nonatomic) IBOutlet UILabel *labelCountdownTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStartTimer;
#property (strong, nonatomic) IBOutlet UIButton *buttonStopTimer;
- (IBAction)startTimer:(id)sender;
- (IBAction)stopTimer:(id)sender;
CountdownTimerViewController.m
#implementation CountdownTimerViewController
#synthesize labelCountdownTimer;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//Instatiating Appdelegate
if(!appdelegate)
appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) viewDidAppear:(BOOL)animated {
if ([appdelegate.countdownTimer isValid]) {
[self updateLabel];
} else {
labelCountdownTimer.text = #"00:00:00";
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Button Action Methods
- (IBAction)startTimer:(id)sender {
[self updateCounter];
}
- (IBAction)stopTimer:(id)sender {
[appdelegate.countdownTimer invalidate];
labelCountdownTimer.text = #"00:00:00";
}
int countLimit=30; //seconds
NSDate *startDate;
- (void)updateCounter {
labelCountdownTimer.text = #"00:00:00";
startDate = [NSDate date];
appdelegate.countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(countDown)
userInfo:nil
repeats:YES];
}
- (void)countDown {
if([[NSDate date] timeIntervalSinceDate:startDate] >= countLimit) {
[appdelegate.countdownTimer invalidate];
return;
}
else {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = -([currentDate timeIntervalSinceDate:startDate]);
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
appdelegate.timeString = [dateFormatter stringFromDate:timerDate];
labelCountdownTimer.text = appdelegate.timeString;
}
}
- (void) updateLabel {
if ([appdelegate.countdownTimer isValid]) {
labelCountdownTimer.text = appdelegate.timeString;
[self performSelector:#selector(updateLabel) withObject:nil afterDelay:0.05];
}
}
There may be problem in your IBOutlet....
Try to create a programatic UILabel and pass the _mainTimerLabel value to that label....
This may help you..
I have a custom uiswitch class, nothing special, the header looks like this:
#import <Foundation/Foundation.h>
#class Course;
#class Student;
#interface AttandanceSwitch : UISwitch
#property (strong,nonatomic) Course *course;
#property (strong,nonatomic) Student *student;
#property (strong,nonatomic) UISwitch *originalSwitch;
-(id)initWithSwitch:(UISwitch *)newSwitch Student:(Student *)student andCourse:(Course *)course;
-(NSString *) description;
#end
the .m file looks as follows:
#import "AttandanceSwitch.h"
#import "Course.h"
#import "Student.h"
#implementation AttandanceSwitch
#synthesize course,student,originalSwitch;
-(id)initWithSwitch:(UISwitch *)newSwitch Student:(Student *)studentP andCourse:(Course *)courseP
{
self = [super init];
self.course = courseP;
self.student = studentP;
self.originalSwitch = newSwitch;
[self.originalSwitch setOn:YES];
return self;
}
-(NSString *) description
{
return [NSString stringWithFormat:#"Student: %#, Course: %#, Switch: %#",student.name,course.name,[originalSwitch description]];
}
#end
So whats actually the problem - when I create a new object of this class using initWithSwitch, I cant set the originalSwitch value. I even tried to set it statically (see self.originalSwitch setOn:YES above), but even if I do that, the switch is still off.
Any ideas on how to get this working would be very appreciated...
this is a excerpt from usage of this class in tableviewcell:
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
[switchView addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventTouchUpInside];
AttandanceSwitch *customSwitch = [[AttandanceSwitch alloc] initWithSwitch:switchView Student:student andCourse:courseToTrack];
Student and Course get filled allright.
Your code will work if you call it like this in your ViewController's viewDidLoad:
UISwitch *newSwitch = [[UISwitch alloc] init];
AttandanceSwitch *customSwitch = [[AttandanceSwitch alloc] initWithSwitch:newSwitch];
[self.view addSubView:customSwitch.original];
[customSwitch.original setOn:YES];
I have two classes:
RootViewController.h
RootViewController.m
In my RootViewController.h
// in .h file
UITextField* myTextField_;
#property (nonatomic, retain) UITextField* myTextField.
In my RootViewController.m
// in .m file
#synthesize myTextField = myTextField_
// in dealloc
[myTextField_ release]
// in viewDidLoad
UITextField* tf = [[UITextField alloc] init] initWithFrame:CGRectMake(200,6,100,30)];
[nameTextField_ = tf];
[tf release]
My question is,
Does that create any memory leaks? Or will that crash? Are there better ways to create an instance of UITextField so I keep a reference to it? Perhaps
myTextField_ = [[UITextField alloc] init] initWithFrame:CGRectMake(200,6,100,30)];
would that be sufficient?
The simpliest way is to do this like this:
.h:
UITextField *myTextField;
#property (nonatomic, retain) UITextField *myTextField;
.m
#synthesize myTextField;
- (void)viewDidLoad {
myTextField = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)];
}
- (void)dealloc {
[myTextField release];
}
You will have one instance which is allocated and released in most clean way and you will have reference to this textfield all the time.
You should not do [tf release] as you are accessing your variable directly.
If you access it via self. notation that it will be called [tf retain] and then you should release tf. So in your current version all is ok besides line where you are releasing.
[nameTextField_ = tf];
change:
[self setMyTextField:tf]
Yes, this will do:
myTextField_ = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)];
You can also use this:
self.myTextField = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)] autorelease];
(when using the property it will retain, when using directly the member myTextField_ it won't (automatically) retain). Also alloc will set retainCount to 1 so it will eventually need to be released (in your case in dealloc method you can use either [myTextField_ release]; or self.myTextField=nil;);
Not sure what this is (I believe it will show up some compile errors):
[nameTextField_ = tf];
I have GraphicView class that inherits from UIView. Its initWithFrame method is:
#implementation GraphicsView
- (id)initWithFrame:(CGRect)frameRect
{
self = [super initWithFrame:frameRect];
// Create a ball 2D object in the upper left corner of the screen
// heading down and right
ball = [[Object2D alloc] init];
ball.position = [[Point2D alloc] initWithX:0.0 Y:0.0];
ball.vector = [[Vector2D alloc] initWithX:5.0 Y:4.0];
// Start a timer that will call the tick method of this class
// 30 times per second
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/30.0)
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
return self;
}
Using Interface Builder I've added a UIView (class = GraphicView) to ViewController.xib. And I've added GraphicView as a property:
#interface VoiceTest01ViewController : UIViewController {
IBOutlet GraphicsView *graphView;
}
#property (nonatomic, retain) IBOutlet GraphicsView *graphView;
- (IBAction)btnStartClicked:(id)sender;
- (IBAction)btnDrawTriangleClicked:(id)sender;
#end
But with this code doesn't work, I need to call [graphView initWithFrame:graphView.frame] to make it works.
- (void)viewDidLoad {
[super viewDidLoad];
isListening = NO;
aleatoryValue = 10.0f;
// Esto es necesario para inicializar la vista
[graphView initWithFrame:graphView.frame];
}
Am I doing well? Is there a better way to do that?
I don't know why initWitFrame is not called if I add GraphicView as a property.
initWithFrame is not called when loading from a NIB, it's initWithCoder instead.
If you may be using both loading from a NIB and programmatic creation, you should make a common method (initCommon maybe?) that you would call from both initWithFrame and initWithCoder.
Oh, and your init method is not using the recommended practices:
- (id)initWithFrame:(CGRect)frameRect
{
if (!(self = [super initWithFrame:frameRect]))
return nil;
// ...
}
You should always check the return value of [super init...].