View Controller Lifecycle
UITextView
- Scrollable, editable/selectable view of mutable attributed string
- View Controller Lifecycle
- Finding out what is happening as a VC is created, hooked up to the view, appears/disappears etc.
NSNotification
- The "radio station" communication mechanism of MVC.
- For now just "tuning in".
- Demo
UITextView
USAttributedString, NSMutableAttributedString, NSTextStorage
UIFont, UIColor
NSNotification
- View Controller Lifecycle
View Controller Lifecycle
- View Controllers have a "Lifecycle"
- A sequence of messages is sent to them as they progress through it.
- Why does this matter?
- You very commonly override these methods to do certain work.
- The start of the lifecycle …
- Creation.
- MVCs are most often instantiated out of a storyboard (as you've seen).
- There are ways to do it in code (rare) as well, perhaps we will see this later.
- What then?
- Outlet setting.
- Appearing and disappearing.
- Geometry changes.
- Low-memory situations.
- At each stage, iOS invokes method(s) on the controller …
- After instantiation and outlet-setting,
viewDidLoad
is called - This is an exceptionally good place to put a lot of setup code.
-
- (void)viewDidLoad { [super viewDidLoad]; // always let super have a chance in setup methods // Do some setup of my MVC … }
- But be careful because the geometry of you view (its
bounds
) is not yet set! - At this point, you can't be sure you're on an iPhone 5-size screen or an iPad or ???.
- So do not initialize things that are geometry dependant here.
- Just before the view appears on screen, you get notified
- (argument is just whether you are appearing instantly or over time via animation)
- (void)viewWillAppear:(BOOL)animated;
- Your view will only get "loaded" once, but it might appear or disappear a lot.
- So don't put anything in this method that really wants to be in
viewDidLoad
. - Otherwise, you might be doing something over and over unnecessarily.
- Do something here if things you display are changing while your MVC is off screen.
- Later we will use this to optimise performance by waiting until this method
- (as opposed to
viewDidLoad
) to kick off an expensive operation (probably in another thread). - And you get notified when you will disappear off screen too
- This is where you put "remember what's going on" and clean up code.
-
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // call super in all the viewWill/viewDid… methods // let's be nice to the user and remember the scroll position that they were at … [self rememberScrollPosition]; // we'll have to implement this, of course // do some other clean up now that we've been removed from the screen [self saveDataToPermanatStore]; // maybe do in did instead? // remember not to do anything time consuming here, or app will be sluggish // maybe even kick off a thread to do what needs doing here (again, we'll cover threads later) }
- There is a "did" version of both of the appearance methods
- (void)viewDidAppear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated
- Geometry changed?
- Most of the time this will be automatically handled with Autolayout.
- (void)view{Will/Did}LayoutSubviews;
- Called any time a view's
frame
changed and itssubviews
were thus re-layed out. - For example, autorotation.
- You can reset the frames of your subviews here or set other geometry-affecting properties.
- Between "will" and "did", autolayout will happen, we will see this a little later on.
- Autorotatoin
- When the device is rotated, the top level view controller will have its bounds reorientated if …
- The view controller returns YES from
shouldAutoRotate
. - The view controller returns the new orientation in
supportedInterfaceOrientations
. - The application allows rotation to that orientation (defined in Info.plist file).
- Generally it is a good idea to try to support rotation in MVCs, we will look at this in some detail later.
- Specific notification that rotation will/did happen
-
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)anOrientation duration:(NSTimeInterval)seconds; - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)anOrientation duration:(NSTimeInterval)seconds; - (void)didRotateFromInterfaceOrientatoin:(UIInterfaceOrientation:anOrientation;
- This property will have the current orientation when each of the above is called …
@property UIInterfaceOrientation interfaceOrientation;
- It's pretty rare to implement these. Autolayout and
viewWillLayoutSubviews
usually suffice. - In low-memory situations,
didRecieveMemoryWarning
gets called … - This rarely happens, but well-designed code with bit-ticket memory use might anticipate it.
- Examples: images and sounds.
- Anything "big" that can be recreated should probably be released (i.e. set
strong
pointer tonil
). awakeFromNib
- This method is sent to all objects that come out of a storyboard (including your Controller).
- Happens before outlets are set! (i.e. before the MVC is "loaded")
- Put code somewhere else if at all possible (e.g.
viewDidLoad
orviewWillAppear:
). - Anything that would go in your Controller's
init
method would have to go inawakeFromNib
too - (because init methods are not called on objects that come out of storyboards).
-
- (void)setup; // something that can't wait until viewDidLoad - (void)awakeFromNib { [self setup]; } // UIViewController's designated initializer in initWithNibName:bundle: (ugh!) - (instancetype)initWithNibName:(NSString *)name bundle:(NSBundle *)bundle { self = [super initWithNibName:name bundle:bundle]; [self setup]; return self; }
- We will not likely need to use
awakeFromNib
at this time. - Summary
- Instantiated (from storyboard - many ways for this to happen, we will see later)
awakeFromNib
- outlets get set
viewDidLoad
- (when geometry is determined)
viewWillLayoutSubviews
andviewDidLayoutSubviews
- (next group can happen repeatedly as your MVC appears and disappears from the screen …)
viewWillAppear
andviewDidAppear
- (whenever geometry changes again while visible, e.g. device rotation)
viewWillLayoutSubviews
andviewDidLayoutSubviews
- (if it is autorotation, then you also get will/didRotateTo/From messages--rare to use these)
viewWillDissapear
andviewDidDissapear
- (possibly is memory gets low …)
didReceiveMemoryWarning
- (there is no "unload" any more, that's all there is)
Demo
- Attributor
- View Controller Lifecycle
- How (and when in VCL) would we "outline" the text on the outline button?
NSNotification
- Notificaions
- The "radio station" from the MVC slids.
NSNotificationCenter
- Get the default "notification center" via
[NSNotificaionCenter defaultCenter]
- Then send it the following message if you want to "listen to the radio station":
-
- (void)addObserver:(id)observer // you (the object get notified) selector:(SEL)methodToInvokeIfSomethingHappens name:(NSString *)name // name of station (a constant somewhere) object:(id)sender // whose changes you're interested in (nil is anyone's)
- You will then be notified when there are broadcasts
-
- (void)methodToInvokeIfSomethingHappens:(NSNotification *)notification { notification.name // the name passed above notification.object // the object sending you the notification notification.userInfo // notificaion-specific information about what happened }
- Be sure to "tune out" when done listening
[center removeObserver:self];
- or
[eenter removeObserver:self name:UIContentSizeCategoryDidChangeNotification object:nil];
- Failure to remove yourself can sometimes result in crashes.
- This is because the
NSNotificaionCenter
keeps an unsafe "retained" pointer to you. - A good place to remove yourself is when you MVC's view goes off screen.
- We'll see how to find out about that a little later.
- Or you can remove yourself in a method called
dealloc
(called when you leave the heap). -
- (void)dealloc { // be careful in this method! Can't access properties! You are almost gone from the heap! [[NSNotificationCenter defaultCenter] removeObserver:self]; }
- Example
- Watching for changes in the size of preferred fonts (user can change this in settings) …
-
NSNotificationCenter *center = [NSNotificaionCenter defaultCenter]; [center addObserver:self selector:@selector(preferedFontSizeChanged:) name:UIContentSizeCategoryDidChangeNotification object:nil]; // this station's broadcasts aren't object-specific - (void)preferredFontSizeChanged:(NSNotification *)notification { // re-set the fonts of objects using preferred font }
//
// ViewController.m
// Attributor
//
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextView *body;
@property (weak, nonatomic) IBOutlet UILabel *headline;
@property (weak, nonatomic) IBOutlet UIButton *outlineButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSMutableAttributedString *title =
[[NSMutableAttributedString alloc] initWithString:self.outlineButton.configuration.title];
// @3 without a negative, the outline has no fill in the centre
[title setAttributes:@{ NSStrokeWidthAttributeName : @3,
NSStrokeColorAttributeName : self.outlineButton.tintColor }
range:NSMakeRange(0, [title length])];
[self.outlineButton setAttributedTitle:title forState:UIControlStateNormal];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self usePreferredFonts];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferredFontsChanged:)
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}
- (void)preferredFontsChanged:(NSNotification *)notification
{
[self usePreferredFonts];
}
- (void)usePreferredFonts
{
self.body.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
self.headline.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
}
- (IBAction)changeBodySelectionColorToBackgroundColorOfButton:(UIButton *)sender {
[self.body.textStorage addAttribute:NSForegroundColorAttributeName
value:sender.backgroundColor
range:self.body.selectedRange];
}
- (IBAction)outlineBodySelection:(id)sender {
[self.body.textStorage addAttributes:@{
// @-3 a negative instructs to also fill center of stroke
NSStrokeWidthAttributeName : @-3,
NSStrokeColorAttributeName : [UIColor blackColor] }
range:self.body.selectedRange];
}
- (IBAction)unOutlineBodySelection:(id)sender {
[self.body.textStorage removeAttribute:NSStrokeWidthAttributeName
range:self.body.selectedRange];
}
@end