踩坑:UINavigationItem 动画,PHPhotos 和磁盘文件读取

UINavigationItem 动画

UINavigationItem 左右两边都能设置 UIBarButtonItem,由于 UIBarButtonItem 不是 UIView 子类,所以无法直接对其进行动画。UIBarButtonItem 有个 customView 属性,可以利用这个来动画,但是定义了这个属性后,UIBarButtonItem原本的 target-action 模式将失效,为了使其能够继续响应触摸事件,最好使用 UIButton,即能响应触摸事件,也能对其动画。
实际中,这么做还是有很多麻烦的。如果要保持 UIBarButtonItem 的外观和风格与原来一致,得需要还原度很高的图片素材:尺寸保持一致,能够响应 tintColor。很难找到这样的素材,对于不会画画的我而言,无解。比如对于 + 这个符号的 UIBarButtonItem,找不到对应的素材,在 UIButton 中利用 titleLabel 来设置,字体风格不搭。

titleView 动画

titleView scale appear and disappear

上图是一个对 titleView 进行缩放显示的动画。一般的思路是直接对 titleView 进行动画。

self.navigationItem.titleView = self.segmentedControl;
self.navigationItem.titleView.transform = CGAffineTransformMakeScale(0.1, 0.1);
[UIView animateWithDuration:0.5
                      delay:0
     usingSpringWithDamping:1
      initialSpringVelocity:1
                    options:UIViewAnimationOptionShowHideTransitionViews
                 animations:^{
                         self.navigationItem.titleView.transform = CGAffineTransformMakeScale(1, 1);
                     }
                 completion:^(BOOL finished){
                         [self.collectionView reloadData];
}];

这时候还没有问题,但是想要 titleView以同样的风格消失时,直接对 titleView 进行缩放动画的结果是缩放后比例不对,而且 titleView 的位置也移动了,甚至是相对于与leftBarButtonItem 的距离按照动画中指定的Y 轴比例进行移动的。在 stackoverflow 上有人也遇到类似问题,由于不清楚内部的实现,为了规避这个问题,方法就是将要动画的视图封装进一个 UIView 再赋值给 titleView,并只在该视图上进行动画,而不是在 titleView 上进行动画。
先封装:

UIView *backView = [[UIView alloc] initWithFrame:self.segmentedControl.bounds];
[backView addSubview:self.segmentedControl];
self.navigationItem.titleView = backView;
    /*缩放动画*/

在segmentedControl 上进行动画,不能在 backView 或是 titleView 上动画。猜测是对视图的 frame 做更改时而没有调整子视图的布局造成的,有谁能去回答一下?

UIView *backView = self.navigationItem.titleView;
UIView *segmentedControl = backView.subviews.firstObject;

[UIView animateWithDuration:0.3
                      delay:0
     usingSpringWithDamping:1
      initialSpringVelocity:1
                    options:UIViewAnimationOptionLayoutSubviews
                 animations:^{
                         segmentedControl.transform = CGAffineTransformMakeScale(0.1, 0.1);
                     }
                 completion:^(BOOL finished){
                         self.navigationItem.titleView = nil;
                         self.segmentedControl.transform = CGAffineTransformMakeScale(1, 1);
                         self.navigationItem.title = self.assetCollection.localizedTitle;
}];

PHPhotos 更新与视图更新

对相册进行操作,添加或删除照片时,通过PHPhotoLibrary 的
- (void)performChanges:(dispatch_block_t)changeBlock completionHandler:(void (^)(BOOL success, NSError *error))completionHandler
方法来提出请求。真正执行数据更新是在
- (void)photoLibraryDidChange:(PHChange *)changeInstance
这要求注册为 PHPhotoLibrary 的观察者:

[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];

在请求的方法中,completionHandler的名字误导让我以为其会在- (void)photoLibraryDidChange:(PHChange *)changeInstance执行完毕后调用,实际上它是在changeBlock 结束后在执行 changeBlock 的串行队列中调用。因此还是在- (void)photoLibraryDidChange:(PHChange *)changeInstance中更新数据的同时更新 UI 吧。好吧,这是我文档没看仔细。

磁盘文件读取(这是个老坑了)

如果你希望将图像保存在应用的 Documents 或 Library 目录下,并且希望在下次启动后使用[UIImage imageWithContentsOfFile:]从磁盘中获取图像时,不要试图保存图像文件的绝对路径。每次应用的升级会造成这个绝对路径发生变化(多谢 @千飞若逸Fee 关于这条的补充),而且从 iOS 8 开始这两个目录的不再与你的应用 Bundle 绑定,这两个目录的路径值都是动态的,每次应用启动都会是不一样的结果,见官方文档或见我的提问。正确方法是,只保留文件在这两个目录下的相对路径,使用NSFileManager- URLsForDirectory:inDomains:方法来获取这两个目录的路径,再拼成绝对路径来访问。

你可能感兴趣的:(踩坑:UINavigationItem 动画,PHPhotos 和磁盘文件读取)