这节课主要讲关于View Controller的生命周期、ImageView、ScrollView、WebView及它们的demo演示。
View Controller Lifecycle
View Controller的生命周期指它们被创建、存活然后消亡。
View Controller的生命周期的第一部分是创建,这部分基本上由storyboard里面的segue完成,一个segue总是创建一个新的View Controller。在storyboard里有个方法叫做instantiateViewControllerWithIdentifer,你需要一个View在storyboard里的View Controller,你给这个方法一个identifier,然后它就会帮你实例化一个View Controller,然后你的职责就是把这个View放在屏幕上,可以push到navigation controller,也可以是popover。由于这些View Controller是从storyboard里面产生的,在ios5里几乎从不重载View Controller的指定初始器。
awakeFromNib意味着它由storyboard产生,然后它在split View Controller里,不过那时outlet都没有连接起来。
viewDidLoad在你的UIViewController完全初始化完毕,所有outlet都已经连接的情况下被调用,所以你的控制器已经完全准备好了,虽然还没有显示在屏幕上。所以viewDidLoad是一个放置大部分你原先觉得需要放到指定初始化里去完成的事情的地方。在viewDidLoad里有个要小心的东西就是你的controller的veiw的尺寸,controller有个property叫做veiw,这个view指向你的view的顶层矩形区域。
如果需要一些几何东西展现出来,以下方法会非常适合:
- (void)viewWillAppear:(BOOL)animated;
viewWillAppear在你的控制器的view正要显示的时候被调用,如果你要在最后时刻来调整view的排列或几何特性,这是非常好的时机。由于view的形状这时候已经形成,所以这里是调整subview或者做一些设置或其他东西的好地方。另一个很适合在viewWillAppear里做的就是缓式动作。viewWillAppear是一个去做那些很费资源的事情的地方,不过耗时很长的东西最好是在viewWillAppear里另开一个线程来运行,然后在你的veiw里面放一个小小的spinning wheel。viewWillAppear适合做两件事:一种是在最后时刻延时加载一些高开销操作,另一种就是改变几何特性的事情。
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self rememberScrollPosition];
[self saveDataToPermanentStore];
}
此方法在view从屏幕上消失的时候被调用,现在veiw即将消失但还在屏幕上显示着,所以现在可能是要记录你的view的状态,这可能是你把东西长期保存起来的地方。另开线程来处理任何UI的改变,或者不怎么费资源的话就直接写入硬盘。
viewWillAppear和viewWillDisappear都有一个did版本,可以在显示之后调用,比如想在view出现之后搞些动画效果,这时候就可以用viewDidAppear:
- (void)viewDidAppear:(BOOL)animated; - (void)viewDidDisappear:(BOOL)animated;
这些will和did都需要调用 super版本方法。在方法开始还是结尾调用super,随自己而定。如果要在viewWillAppear里做一些基于父类的事情,可以先调用再做;如果要在父类的viewWillAppear之前做一些设置,就可以先做完再调用。
frame改变了,如果仅仅调节struts and spring还不够好怎么办,在由frame的改变而触发输出subview之前,viewWillLayoutSubviews被调用,viewDidLayoutSubviews在之后被调用。更加详细的掌控autorotation:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)anOrientation duration:(NSTimeInterval)seconds; - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOriention)orient duration:(NSTimeInterval)seconds; - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)anOrientation; @property UIInterfaceOrientation interfaceOrientati
viewDidUnload方法是生命周期里一个低内存时的情况, 这会在低内存的情况下发生,系统会卸载你的view。意味着系统将把你controller的view从内存中清除出去,也就是停止所有strong指向它的指针,系统将要把它的strong指针设为nil。在这之后,viewDidUnload会被调用,只有在你的self.view被设置为nil之后这方法才被调用,最好是不管怎样,手动在viewDidUnload里把所有的outlet设置为nil。
- (void)viewDidUnload { self.faceView = nil; }
不管内存有多么低,在屏幕上的view都不会被unload,所以只针对屏幕外的view controller。view controller本身不会被从heap中清除掉的,被清除的仅仅是它的view。
在没有storyboard之前,是用一些xib文件的东西来allocate和init viewcontroller。在没有storyboard或xib的情况下,创建一个UIViewController是可行的吗?主要就是利用alloc/init。不会想要用alloc/init来创建你的UI,因为这样在storyboard里就看不到view controller了,就是不能看到用户界面的流程了。正确的途径就是重载loadVeiw方法,loadVeiw这个方法有着唯一的职责就是设置self.view,一般情况下不设置。除非在loadVeiw里,永远不要让self.view指向其他任何东西。但是用了storyboard就根本不需要再去实现loadVeiw,所以就永远不要让self.view等于任何东西。loadVeiw只是为了极少情况下,那些不是从storyboard里实例化的view controller准备的。
如果可以的话应尽量避免使用awakeFromNib,在这里可以进行早期的初始化,但是放到viewDidLoad要好的多,因为这时候所有的outlet都已经设置完了。
谁负责设置UIView的frame?答案是,把view放到view hierarchy里面的那个对象。一般情况下这个对象是谁?是storyboard。
UIImageView
它是UIView的一个子类,仅仅显示UIImage。怎么设置UIImage?可能会在storyboard里面设置,不过有时图片可能会在运行被操控,这种情况下在UIImageVeiw上有个property叫image,只需让image等于UIImage的一个实例。也可以用一张图片来alloc/init,这时UIImageVeiw的frame、bound、size、width会被设置成你的图片的尺寸。但是当用property来设置图片时,它不会自动调整frame。
- (id)initWithImage:(UIImage *)image; // will set its frame’s size to match image’s size @property (nonatomic, strong) UIImage *image; // will not adjust frame size
记住UIView’s contentMode property,contentMode确定了UIImage在UIImageVeiw里面是怎么绘制的。
UIImageVeiw可以有一个自己的highlighted version,然后有一个property来设置哪个正在使用。
UIWebView
一个在view里面的完整的因特网浏览器,它是一个展示了完整的因特网浏览器的UIView的子类。它基于WebKit,不过是一个开源的HTML渲染框架。它支持javascript,不过只能运行5秒,所以不能执行一些javascript的循环使你的手机锁住,javascript也只能分配10M的内存。
它有一个非常重要的property,就是:
@property (nonatomic) BOOL scalesPagesToFit;
这个property决定了网站是否要缩小来适应你的UIWebView,如果不是,它将是它原来的大小,而且也不能scroll,所以几乎不能让这个值设置成no,不过no是它的默认值。
在ios5里,webVeiw通过这个scrollVeiw这个property来实现滚动:
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
可以让scalesPagesToFit等于no,然后打开scrollVeiw来滚动,但还可用scrollVeiw来控制webVeiw的滚动方式。
有以下三种方法来加载HTML:
- (void)loadRequest:(NSURLRequest *)request; - (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL; - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEtype textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL;
最好加载HTML的方法就是第一个,另外两个里面的baseURL,就是与HTML中所有链接都相关的URL。MIMEType的作用是如何解析传入的数据,因为传入的数据可以简单看成NSData,你需要知道究竟是什么类型,而MIMEType会告诉你。但我们主要利用NSURLRequest来做这些事:
NSURLRequest + (NSURLRequest *)requestWithURL:(NSURL *)url; + (NSURLRequest *)requestWithURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy timeoutInterval:(NSTimeInterval)timeoutInterval;
为什么用NSURLRequest而不是NSURL?有一种方法可以将NSURL转换成NSURLRequest,通过使用NSURLRequest类。NSURL和NSURLRequest的区别是,你可以指定缓存策略,比如不从网上下载网页,而只是从本地缓存中加载,如果不指定,会采用默认设置。NSURL中的URL和NSString非常相似,但它有更良好的格式,它会像file://接着是一个文件路径。
+ (NSURL *)urlWithString:(NSString *)urlString;
+ (NSURL *)fileURLWithPath:(NSString *)path isDirectory:(BOOL)isDirectory;
ScrollView
我们想要知道scrollVeiw现在正在哪显示,一种办法是找到可视区域的左上角,这个左上角是一个在scrollVeiw中名字为contentOffset的属性,就是在content area的坐标系中距离左上角的x、y距离。
另一种方法是看scrollVeiw的边界,当你尝试找出scrollVeiw的bound是什么的时候,可以调用标准view方法convertRect: toView:来做这件事,它会给出在content area坐标系中的scrollVeiw bound:
CGRect visibleRect = [scrollView convertRect:scrollView.bounds toView:subview];
需要考虑坐标系问题,当处理数学问题时,一定要保持坐标系不变;当你处理一些计算,要保持你等式的两部分在相同的坐标系中,或者在scrollVeiw中,或者在content area中。
如何建一个scrollVeiw呢?可以用alloc/initWithFrame,这样建的只是普通的view,然后设置content area,开始添加subview。但我们经常会把它们从storyboard中取出来,怎么把它们放入storyboard中?选取一个具有滚动效果的view,在xcode的编辑菜单下,有一个embed选项,在view的scrollVeiw中有一个embed选项。如果你在scrollVeiw中设置了embed,它会在你的view周围放置一个scrollVeiw。用编程的方式加入subview,只要用addSubview。不要忘记设置content的大小,必须把content的大小设得足够大,这样才能实现滚动。想要使scrollVeiw能顺利运行,指定它在多大的区域内滚动是很重要的。
可以调用以下方法,用编程的方式实现滚动:
- (void)scrollRectToVisible:(CGRect)aRect animated:(BOOL)animated;
这里你在content area坐标系里指定了一个矩形,然后它会变得可见。这个方法会最小化所需要的滚动量,以使其尽量都是可见的。大多数情况下,用户通过触摸来使其滚动。
在scrollVeiw中另一件主要的事就是缩放,缩放需要做一些处理,所有的处理都是在UIView上设置transform property,所有的UIView都有transform这个property。transform是一种affine transform,基本上就是尺度、透明度和旋转变换。当你设置transform review时,你就是在影响它的bit位,view没在屏幕上画,而是在一些缓存上画,然后将这些bit、像素拷贝到屏幕上。当你进行affine transform,你改变它的大小,就是改变bit位的大小,并不是把你的view重绘的更大。如果你旋转它,就是旋转这些bit。通常不这样做,因为bit上的操作并不是我们想要的,也许在转换和旋转时会这样做,但改变尺度时不这样做。当UIScrollView放大时就是改变那些bit位的尺度,因为它只是设置这个property。
也可以在content area中的view上进行缩放操作,不能进行整体放大。整体缩放就是建立一个UIView包含所有的空间,并让所有其他的view作为它的子视图。
怎样缩放呢?一是你要设置缩放的最大和最小倍数,把min设为0.5(就是最多缩小到原始大小的一半),把max设为2(就是放大倍数最多为原始大小的两倍),这两个默认值都是1。
scrollView.minimumZoomScale = 0.5; // 0.5 means half its normal size scrollView.maximumZoomScale = 2.0; // 2.0 means twice its normal size
二是要设置delegate,必须要设置scrollVeiw的delegate,否则它不会进行缩放,这是因为你必须实现一个叫做viewForZoomingInScrollView的方法,它返回scrollVeiw中你想进行变形的那个子view:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)sender;
还有一种动态的缩放方式,它会使这些改变动态的呈现在屏幕上,然后你可以zoomToRect,用张图片来展示zoomToRect:
这是缩放尺度。如果把缩放尺度下调到1,它就变小。这就是缩放尺度的设置。
接下来是zoomToRect,用白色的矩形来标识矩形:
接下来它会把矩形放大最小的倍数,来适应scrollVeiw。要是矩形比原先的更大,就用zoomToRect,它会缩小最小的倍数来适应屏幕。