In a ViewController I have the following:
- (void)viewWillAppear:(BOOL)animated
{
DataObject *theDataObject = [self theAppDataObject];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"MMM dd, yyyy HH:mm"];
NSString *dateStr = [formatter stringFromDate:theDataObject.deadline];
NSLog(#"Logged dateStr: %#", dateStr);
[dateTimeLabel setText:dateStr];
[super viewWillAppear:animated];
}
To clarify: dateTimeLabel IS wired up in the xib file. The viewWillAppear method is explicitly called from another ViewController, and is firing, like so:
- (IBAction)setDateTimeButtonClicked:(id)sender
{
DataObject *theDataObject = [self theAppDataObject];
theDataObject.deadline = [datePicker date];
FirstMobileViewController *mobileVC = [[FirstMobileViewController alloc] init];
[mobileVC viewWillAppear:YES];
[mobileVC release];
[UIView transitionWithView:self.view.superview
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight | UIViewAnimationOptionLayoutSubviews | UIViewAnimationOptionAllowAnimatedContent
animations:^{[self.view removeFromSuperview];}
completion:NULL];
}
The viewWillAppear method is firing -- the dateStr is logged by NSLog appropriately when the superview is shown again. But the dateTimeLabel never updates. Obviously, commenting the NSLog line doesn't make a difference.
The MADDENING thing is that even though NSLog logs dateStr just fine, if I change dateStr to, say, #"Yo!" or even to a locally initialized string, then dateTimeLabel will update, no problem.
What am I missing here?
You method to add a child view controller isn't correct. Try with the following code (with your method, when you call ViewWillAppear, think that the view of the view controller isn't yet initialized. (you san check that with a simple hack: adding mobileVC.view; just after the mobileVC initialization)
- (IBAction)setDateTimeButtonClicked:(id)sender {
DataObject *theDataObject = [self theAppDataObject];
theDataObject.deadline = [datePicker date];
FirstMobileViewController *mobileVC = [[FirstMobileViewController alloc] init];
[self addChildViewController:mobileVC];
UIView *superView = self.view.superview;
mobileVC.view.frame = superView.bounds;
[UIView transitionWithView:superview
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight | UIViewAnimationOptionLayoutSubviews | UIViewAnimationOptionAllowAnimatedContent
animations:^{[self.view removeFromSuperview];
[superview addSubView:mobileVC.view]}
completion:^(BOOL finished) {
[mobileVC didMoveToParentViewController:self];
}];
}
With this method, the viewWillAppear should be called automatically.
Firstly, you're releasing mobileVC before doing anything with it, so while it runs the code in the viewWillAppear: method, that's all that happens.
You're also not loading the XIB file for mobileVC, since you only alloc and init it. Therefore the objects you reference within viewWillAppear most likely don't exist at the point that you explicitly call the method.
You haven't shown the code that you use to present the mobileVC so it's hard to guess what the fix will be, or to understand the quirky problem you're having with string literals working. But, suffice to say, when you present the new view controller, it will be a different instance than the one you explicitly called.
Possible fix:
Use initWithNibName:bundle: instead of plain init to ensure that your XIB is loaded. Then pass mobileVC into the completion block of your transition to present it?
Put [super viewWillAppear:animated] at the beginning of viewWillAppear.
Maybe you never connect dateTimeLabel in xib to dateTimeLabel.
Try to use [self. dateTimeLabel setText: dateStr]
Related
My calendar is working perfectly I show events from calendar in my tapku calendar and I can also edit them so for editing I am using EKEventEditViewController so when editing is completed then
- (void)eventEditViewController:(EKEventEditViewController *)controller
didCompleteWithAction:(EKEventEditViewAction)action
get called and this is how I try to update My calendar here
TKCalendarMonthViewController *tk = [[TKCalendarMonthViewController alloc]init];
[tk loadView];
loadview method is as below and now I will explain whats wrong in comment
this method get called when My calendar is loading. So for loading it again I am calling this method again
- (void) loadView{
[super loadView];
_monthView = [[TKCalendarMonthView alloc] initWithSundayAsFirst:_sundayFirst];
_monthView.delegate = self;
_monthView.dataSource = self;
NSLog(#"::%#",_monthView.dataSource);
NSLog(#"::%#",_monthView.delegate);
[self.view addSubview:_monthView];
[_monthView reload]; // reload method get called
}
- (void) reload{
NSArray *dates = [TKCalendarMonthTiles rangeOfDatesInMonthGrid:[currentTile monthDate] startOnSunday:sunday];
NSLog(#"%#",dates);
NSLog(#"%#",self.dataSource);
// below calendarMonthView:self method get called when my cal is getting called first time by it self but when i try by creating object of class like i showed in that delegate method then below method is not getting called and this is why my cal is not getting updated
// SO THE PROBLEM IS THIS METHOD IS NOT GETTING CALLED
NSArray *ar = [self.dataSource calendarMonthView:self marksFromDate:[dates objectAtIndex:0] toDate:[dates lastObject]];
TKCalendarMonthTiles *refresh = [[TKCalendarMonthTiles alloc] initWithMonth:[currentTile monthDate] marks:ar startDayOnSunday:sunday];
[refresh setTarget:self action:#selector(tile:)];
[self.tileBox addSubview:refresh];
[currentTile removeFromSuperview];
currentTile = refresh;
}
All NSLog's are prints with values non of them is null.
#property (strong,nonatomic) TKCalendarMonthView *monthView;
[self.monthView reload];
note: do not #synthesize property
I have the following code that updates the value of a textfield with the current time. My question is: Why do you send nil as the sender at the bottom part of the code? [self showCurrentTime:nil];
CurrentTimeViewController.m
- (IBAction)showCurrentTime:(id)sender
{
NSDate *now = [NSDate date];
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setTimeStyle:NSDateFormatterShortStyle];
}
[timeLabel setText:[formatter stringFromDate:now]];
}
...
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"CurrentTimeViewController will appear");
[super viewWillAppear:animated];
[self showCurrentTime:nil];
}
Because normally when action handlers are called, they pass the object that initiated the call to the method, like a UIButton, or UISegmentedControl. But, when you want to call the method from your code, and not as the result of an action, you cannot pass a human as sender, so you pass nil.
Also, the - (IBAction) indicates that this method can be connected to an event via the Interface Builder by dragging a Touch Up Inside (or touch down outside/etc) event from a button (or any other control that has some sort of event) to the File's Owner and selecting thumbTapped:.
For example:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = 1001;
[button addTarget:self action:#selector(thumbTapped:) forControlEvents:UIControlEventTouchUpInside];
When the touch is released (and the touch is still inside the button), will call thumbTapped:, passing the button object as the sender
- (IBAction)thumbTapped:(id)sender {
if ([sender isKindOfClass:[UIButton class]] && ((UIButton *)sender).tag == 1001) {
iPhoneImagePreviewViewController *previewVC = [[iPhoneImagePreviewViewController alloc] initWithNibName:#"iPhoneImagePreviewViewController" bundle:nil];
[self.navigationController pushViewController:previewVC animated:YES];
[previewVC release];
} else {
[[[[UIAlertView alloc] initWithTitle:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"]
message:#"This method was called from somewhere in user code, not as the result of an action!"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease] show];
}
}
IBAction methods, in their most common form, take a single sender argument. When invoked by the system, they pass the control that triggered the action as the sender parameter. If you're going to call the method directly, you'll need to provide a sender. Since this method isn't being invoked by a control that's just been interacted with by the user, nil is used.
I actually think that calling actions directly isn't a good pattern to follow. Having a method with the tag IBAction implies "This method is invoked in response to user action" and that's an important bit of context to be able to depend on when working within that method. If code is going to call the method directly that idea's been violated.
Usually I think it's better to do something like this:
- (void)updateTime:(NSDate *)date {
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setTimeStyle:NSDateFormatterShortStyle];
}
[timeLabel setText:[formatter stringFromDate:date]];
}
- (IBAction)showCurrentTime:(id)sender {
[self updateTime:[NSDate date]];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateTime:[NSDate date]];
}
It's because you need to send something, since the signature of the method you're calling takes an argument.
The :(id)sender is commonly seen in button press actions. When you connect a button in a nib to a method in your view controller, it will check to see if it can take an argument. If it does, the "sender" will point to the instance of the UIButton that was created in the nib.
This is also true for programmatic button creation, and many other cases where you send in selectors.
The specifcation for the showCurrentTime function has 1 argument named sender. If you called the function without sending the ID of an object the call would be invalid.
Nil is used instead of NULL in objective-c and it is purely being sent to satisfy the specification of the function you are calling.
As the function does not actually use the argument within the body it does not actually matter what object you send to the function.
I don't know what's going wrong here. The crash happens when switching back and forth between views.
Here's what instruments gives me:
Clicking into it references this code with the first action :
-(IBAction)pushnews; {
NewsViewController *news = [[[NewsViewController alloc]init]autorelease];
news.title =#"Page";
[self.navigationController pushViewController:news animated:YES]; }
I use autorelease sometimes but usually I just release it my self. Should I get rid of autorelease and add [news retain]
What am I doing wrong?
Edit based on answers:
Following EmptyStack's Advice: ViewWillDisappear Code looks like this:
- (void)viewWillDisappear:(BOOL)animated {
webView.delegate = nil; }
This seems to resolve issues (pending more testing)
In viewdidload I said: webView.delegate = self;, which may have been the issue!
My guess is that, there is a UIWebView in NewsViewController, and it is causing the crash. It is possible that, a delegate method of web view is called after the web view is released. If so, try to setwebView.delegate = nil; in NewsViewController's viewWillDisappear: method.
try this instead :
-(IBAction)viewcontroller;
{
NewsViewController *news = [[NewsViewController alloc]init];
news.title =#"Page";
[self.navigationController pushViewController:news animated:YES];
[news release];
}
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 !
I want to update UILabel by clicking a reload button. Additionally, I want to update the label in background, because it is fetching the new data via XML from my website. Of course it would be nice to auto-update the label when the application is opened. And there is my problem:
I was able to make it work well when user were clicking the button manually. But I don't understand how to do the same by calling my method via "applicationDidBecomeActive". I tried to do it the same way, but it obviously doesn't work because my label is returned nil.
I suppose there is a problem of my understanding and the solution should be quite easy. Thanks for your input! Note: I am a beginner with Objective-C and have sometimes problems with "easy" things. ;-)
Below is a summary of the important code parts:
AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[MyViewController alloc] reloadButtonAction];
}
MyViewController
#synthesize label
- (void)reloadButtonAction {
[self performSelectorInBackground:#selector(updateData) withObject:nil];
}
- (void)updateData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Parse the XML File and save the data via NSUserDefaults
[[XMLParser alloc] parseXMLFileAtURL];
// Update the labels
[self performSelectorOnMainThread:#selector(updateLabels) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)updateLabels {
NSUserDefaults *variable = [NSUserDefaults standardUserDefaults];
myLabel.text = [variable stringForKey:#"myLabelText"];
// myLabel is nil when calling all of this via AppDelegate
// so no changes to the myLabel are done in that case
// but: it works perfectly when called via button selector (see below)
NSLog(#"%#",myLabel.text);
}
-(void)viewDidLoad {
// Reload button in the center
UIButton *reloadButton = [UIButton buttonWithType:UIBarButtonSystemItemRefresh];
reloadButton.frame = CGRectMake(145,75,30,30);
[reloadButton setTitle:#"" forState:UIControlStateNormal];
[reloadButton addTarget:self action:#selector(reloadButtonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:reloadButton];
}
First:
[[MyViewController alloc] reloadButtonAction];
Doesn't make sense. You allocate memory, without initializing an object. And then you want to call a method on it. Doesn't work
Use an instance for it:
[myViewControllerInstance reloadButtonAction];
In your app delegate you should have an reference to your rootcontroller instance if that is the object contains the reload method, use that instance.
Note:
Alloc only reserves space in the memory for an object which size the size of MyViewController instance. An init method will fill it.