I want a button that will become available at, say, 8 AM. I know how to check if the button is available when the view is loaded, but, if the user is looking at the button as the clock turns to 8 AM, how do I get the button to become active without loading the view?
The button should always be present. Use its hidden to hide and show it as necessary. Or you can use its enabled to enable or disable it as necessary, but this can require more work, because a disabled button might not automatically look disabled so the user won't understand that it isn't truly "available".
Thus we have three cases to deal with:
The user moves to this view controller, the view controller loads its view, and your code decides about the button state / visibility now. You've already handled this one.
The view controller is already present and the view is visible, and the availability-change moment comes. In this case you will need previously to have set up a notification to prompt the view controller to take action now. Do not loop perpetually watching the time. Use an NSTimer or similar (a local notification is also worth considering).
The view controller is covered by another view controller and then uncovered. Perhaps the state needs to have changed in the meantime. So you will also want to do this check generally in viewWillAppear:, not just in viewDidLoad.
Related
I want to build some analytics into my app and I would like to send some data when user leaves current screen, though there are multiple ways he can do so (back button, other button, sidebar menu, etc). Is there any efficient way to do this? I really don't feel like implementing it to every possible button that can lead the user to different screen.
You should call your function inside viewWillDisappear, which is called every time the current view controller is about to disappear from screen. See the documentation of viewWillDisappear
Also see the view controller life cycle (thanks #Paolo for the tip) below (documentation).
In my iOS application, when the user pushes a button in a view, a NSTimer is trigered in the controller.
On the third tick, I would like to make the background of the view bliking.
I've written the blinking function in the view (it should't be written in the controller, should it ?)
I can trigger this blinking function in the controller by
LostView *lostView = (LostView* ) self.view;
[lostView blinkBackground];
But it's bad, isn't it ? The controller shoudn't know the view neither the name of the function ?
I would like to apply the MVC pattern
Is the observer/obervable pattern applicable in this situation ?
Thanks
No it's not bad at all. It looks like you implemented the method to make the view blink in the view itself. That's fine, because it's directly related to the visual representation (i.e. the view part of MVC). You could reuse that view in any other app that requires a blinking view.
Since that blinking is triggered by an NSTimer I assume that it's somehow dependent on the logic in this specific app. The view can't (shouldn't) know when it's supposed to blink (that would only be the case if that blinking was a direct reaction of an interaction with it or another related part of the UI - or it was part of a more complex element, for example a countdown timer that always starts to blink when it reaches the last 10 secs or so. For example the UIButton provides the possibility to highlight it self if it's touched.)
But if that blinking is a reaction of some state transition in your app, maybe some new data becomes available or a countdown is about to expire, the controller is a perfectly reasonable place to trigger that.
I have a form on a tabbed view; I mark the form dirty of any of the fields have been changed and I want to pop up an ActionSheet with "save"/"cancel" if the form is dirty (in lieu of a "save" button). Is there any way to stop the view from disappearing (or being removed from the view stack) until the user responds to the ActionSheet being handled?
A couple of thoughts:
It's worth noting that Apple's Human Interface Guidelines (the HIG) explicitly discourages this practice. They suggest that apps should "ask people to save only when necessary" because people "should have confidence that their work is always preserved unless they explicitly cancel or delete it." Perhaps in your case, it's important to have this feature, but it's generally discouraged.
An alternative, if you want to give users the chance to revert to old settings is to provide an "undo" button, that way, you honor the HIG and effectively auto-save, but you also give the user to explicitly revert to prior values if they really need to.
As others have noted, the notion of prompting to save or discard on viewWillDisappear doesn't quite work. It's logically too late in the process. viewWillDisappear could be called for too many reasons, many of which are not under your control, and it's not copacetic to fail to return promptly to that method, to introduce new user interface elements, etc.
If you really, really need the "save" vs. "cancel" user interface, then that lends itself to more of a modal interface (or push a new view controller that you have to pop off to return to your tab bar view controller) with save and cancel buttons rather than a tab bar interface. E.g. your tab bar view could show current values, you tap on an "edit" button, which pushes new view with save and cancel buttons. We don't know enough about your app to be able to advise whether this is logical in your case or not. (For another approach, see enabling edit mode in view controller.)
You can't stop the view from disappearing once the app has progressed to the point where viewWillDisappear: has been called. The thing to do would be to create a function like:
- (void)saveAndClose {
//Display sheet asking user what they want to do
}
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
{
if (buttonIndex == 0) // 0 or whatever the index of your save button is
{
// Perform save functions
}
[self dismissModalViewControllerAnimated:YES]; // or pop the view controller if appropriate
}
My ActivityIndicator comes up at the right time but the user can still press the buttons on the view while processing is occurring. The reason I'm using ActivityIndicator is to prevent such usage.
How do I make sure that during the animation of ActivityIndicator, no UI interaction is possible?
Also, I would prefer that the current view is still displayed to the user(as in I wouldn't prefer having an opaque view of UIActivityIndicator-if that's possible-unless that's the only option)
Check out DSActivityView. It's a one-shot singleton that puts up a modal activity view VERY easily.
Include it in a View Controller, and then before you fire a long-running background process, you go:
[DSBezelActivityView activityViewForView:self.view];
And then when you're done and want to retire it, you go:
[DSBezelActivityView removeViewAnimated:YES];
Lots of configuration options, including the ability to put custom messages in the activity view... It's a great little library that I use ALL the time.
ActivityIndicator's job is to like names says indicate that something goes on, and not to disable all interactions...You should be the one to do that...you could try with setting userInteractionEnabled property to NO.
When everything is finished set it back to YES
I have always been a bit unclear on the type of tasks that should be assigned to viewDidLoad vs. viewWillAppear: in a UIViewController subclass.
e.g. I am doing an app where I have a UIViewController subclass hitting a server, getting data, feeding it to a view and then displaying that view. What are the pros and cons of doing this in viewDidLoad vs. viewWillAppear?
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.
It's important to note that using viewDidLoad for positioning is a bit risky and should be avoided since the bounds are not set. this may cause unexpected results (I had a variety of issues...)
This post describes quite well the different methods and what happens in each of them.
currently for one-time init and positioning I'm thinking of using viewDidAppear with a flag, if anyone has any other recommendation please let me know.
Initially used only ViewDidLoad with tableView. On testing with loss of Wifi, by setting device to airplane mode, realized that the table did not refresh with return of Wifi. In fact, there appears to be no way to refresh tableView on the device even by hitting the home button with background mode set to YES in -Info.plist.
My solution:
-(void) viewWillAppear: (BOOL) animated { [self.tableView reloadData];}
Depends, Do you need the data to be loaded each time you open the view? or only once?
Red : They don't require to change every time. Once they are loaded they stay as how they were.
Purple: They need to change over time or after you load each time. You don't want to see the same 3 suggested users to follow, it needs to be reloaded every time you come back to the screen. Their photos may get updated... you don't want to see a photo from 5 years ago...
viewDidLoad: Whatever processing you have that needs to be done once.
viewWilLAppear: Whatever processing that needs to change every time the page is loaded.
Labels, icons, button titles or most dataInputedByDeveloper usually don't change.
Names, photos, links, button status, lists (input Arrays for your tableViews or collectionView) or most dataInputedByUser usually do change.