有段时间没写东西了,最近都是机械搭项目。趁着后台,出接口的这几天。
咱也定一个小目标,比如说-----
算了,不吹牛逼了,怕被打死。
最近刚做一个项目,准备在项目中穿插着写一点东西吧。
1杂碎的知识点
2讲一下项目大体的架构,分析一下项目用的三方的一些问题和选择
3讲一下,我这个菜鸡对项目网络层以及数据存储层的设计以及分析
项目没做完之前,我会不断更新杂碎的知识点这篇,等项目over.和你们好好唠唠项目整体。
好了不Xx了
1.1 UINavigationController
导航栏控制器基本是项目必备啊,所以我们UI大姐们也是想着花样搞这个东西。我们做这方面开发的都是到NVC(码字 有点烦,后面都这样简称了),基本构造做开发的都是知道,是一个容器类 下面的VCs都是放在 stack中的。所以说,在stack里面那么多VC都是公用一个navigationBar啊。UI大姐就喜欢一个VC 拥有一个不同的bar....欲哭无泪。
现在按按修改程度,一点一点写。
1.1.1bar 上的返回按钮太丑了 换掉0.0
~~~~~第二个方法就是整个替换掉leftItem,当然随之而来的问题就是要实现点击返回功能和左滑返回功能。
因为要实现左滑 最好还是重写一个NVC子类 而不是用appearce
```
//遵循手势的协议
@interface IGONavigationController ()<UIGestureRecognizerDelegate>
- (void)viewDidLoad{
[super viewDidLoad];
self.interactivePopGestureRecognizer.delegate = self;
}
//替换掉各个页面的返回按钮
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//设置返回按钮
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"fanhui"] style:UIBarButtonItemStyleDone target:self action:@selector(popViewControllerAnimated:)];
[super pushViewController:viewController animated:animated];
}
//右滑返回的处理
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
BOOL ok = YES; // 默认为支持右滑反回
if ([self.topViewController isKindOfClass:[RootViewController class]] ||
[self.topViewController isKindOfClass:[Root1ViewController class]] ||
[self.topViewController isKindOfClass:[Root2ViewController class]] ||
[self.topViewController isKindOfClass:[Root3ViewController class]]) {
return NO;
}
return ok;
}
```
要注意的是 开起左滑 返回 移动去去掉 NVC 的rootVC 不然就会出现空stack的问题,从而导致各种问题。
######1.1.2bar 有的页面需要,有的页面不需要
这个也是挺烦的,我看到很多同鞋。在处理这个问题上,都会这么写。加入A页面需要,B页面不需要的话
```
A
- (void)viewWillAppear:(BOOL)animated {
self.navigationController.navigationBar.hidden = NO;
}
B
- (void)viewWillAppear:(BOOL)animated {
self.navigationController.navigationBar.hidden = YES;
}
- (void)viewWillDisappear:(BOOL)animated {
self.navigationController.navigationBar.hidden = YES;
}
```
这种做法,也就效果勉强出来了,不过会带来一系列的问题
·····1 左滑 返回的时候NVC 无法分清那个VC 有Bar .出现显示问题
·····2当2个VC对应 statusBar 的字体颜色不一样的时候。上面这种做法,虽然隐藏了Bar 但在VC中重写`preferredStatusBarStyle`依旧没有作用的。NVC很难分清其中的界限。
正确的做法应该是
这种修改Bar 的工作还得交给代理去做,毕竟出现的意外情况,苹果的大牛们都考虑到了,代码要写在改写的地方。
因为我子类化得NVC用的地方很多,所以对于Bar的代理 我基本会放在rootVC中
下面的代码 就是在 SearchViewController和SearchResultController中隐藏了Bar
在ProductDetailViewController中对Bar的样式 进行了修改。
```
//在rootVC 中 遵循NVC代理 UINavigationControllerDelegate
//隐藏导航栏
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 隐藏Bar
BOOL isShowHomePage = [viewController isKindOfClass:[SearchViewController class]] || [viewController isKindOfClass:[SearchResultController class]];
[self.navigationController setNavigationBarHidden:isShowHomePage animated:YES];
UIImage *shadowImage = self.navigationController.navigationBar.shadowImage;
UINavigationBar *bar = self.navigationController.navigationBar;
if ([viewController isKindOfClass:[ProductDetailViewController class]]) {
bar.shadowImage = [UIImage new];
[bar lt_setBackgroundColor:[UIColor clearColor]];
}else {
bar.shadowImage = shadowImage;
[bar setBackgroundImage:[UIImage imageNamed:@"导航栏"] forBarMetrics:UIBarMetricsDefault];
}
}
```
这样 在左滑的时候消除了显示问题,以及在在隐藏了Bar的VC中无法控制statusBar的状态颜色问题。
######1.1.3 每个bar完全不同
童鞋 全隐藏 自定义吧QAQ
#1.2UINavigationBar
大家现在都是最低支持7以上了,太老的东西就不涉及了。
bar 有2个烦人的地方就是偏移量和透明度了
######1.2.1偏移量
我们知道默认`translucent = YES`。就是Bar 是有透明度的。
在有透明度的情况下,系统默认`automaticallyAdjustsScrollViewInsets`属性是YES就是对于scrollerview的子类会默认content偏移64个单位。这样做的目的,既然Bar都是透明的了,系统就觉得你的scrollerView一定在会在(0,0)点,content偏移一个64个单位。在你滑动的时候,隐藏在Bar下面的Content会有一个模糊显示的效果。QAQ
如果你不想要这个偏移量
1 设置translucent = NO,既然Bar不透明。自然不需要偏移量喽
2 关闭这个偏移量,automaticallyAdjustsScrollViewInsets = NO
提一下像tableView,在storyboard 设置上下左右的约束。结果cell上面多了一块空白。就是这个问题。
######1.2.2透明度
这种需求现在太多了,尤其都要做成淘宝那样的详情页,烦的一腿。现在我就在分析一下。
首先如果想滑动控制Bar的透明度,translucent 属性一定要设置成YES(默认)
当Bar 被设置成透明状态的时候
![ED5A2936-7199-47B4-9E14-73E44F49BE74.png](http://upload-images.jianshu.io/upload_images/1106106-6e082fa875411d82.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
还是这张图 _UIBackdropView 这个view是设置半透明毛玻璃效果的view(当然只在透明属性为YES 或者 没有背景图片的时候才会出现)
首先我们要去掉层特殊的视觉图层 利用设置BarBackground图片
```
- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
```
千万不要用translucent = NO 这种方法来去掉视觉图层,都设置不透明了,那我们还玩按什么是吧。。。
这里注意的是我们设置的barTintColor 以及 background 都是对_UINavigationBarground这个Imageview来进行操作的。
这时候 我们就剩下Bar 和 BarBackground两层了
首先我们来说下Bar上面设置颜色的3个方法
```
//背景颜色 继承父类的 不过这个是设置bar 的背景颜色
@property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is nil. Can be useful with the appearance proxy on custom UIView subclasses.
//这个是设置bar 上面的那层 barBackground imaeView的颜色
@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // default is nil
//这个是item的颜色
@property(null_resettable, nonatomic,strong) UIColor *tintColor;
```
再来看下各个控件的高度 bar 本身是44 而barBackgoundImageView 确实64.。
这就是 为什么你设置了bar你颜色 或者 背景图片 。为什么status Bar 的背景也会跟着改变哦。
其实Bar 本身的颜色几乎很少设置,因为现在都要2个bar 一致才符合主流嘛
怎么改变barBackgroundImageView的背景颜色,又成了一个问题。因为刚才为了去掉视觉图层,我们给barBackgroundImageView 赋值了一个空图片。问题出来了,有了图片属性之后,给barBackgroundImageView设置背景颜色就没有用了,现在主流的做法是
查一个view进去 去调节这个 view的颜色 来控制bar 的背景颜色控制
写个类目 给 bar 加一个 - (void)iGo_setBackgroundColor:(UIColor *)backgroundColor
```
- (UIView *)backView
{
return objc_getAssociatedObject(self, &backView);
}
- (void)backView:(UIView *)backView
{
objc_setAssociatedObject(self, &backView, backView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)iGo_setBackgroundColor:(UIColor *)backgroundColor
{
if (!self.backView) {
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.backView = [[UIView alloc] initWithFrame:CGRectMake(0, -20, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + 20)];
[self insertSubview:self.backView atIndex:0];
}
self.backView.backgroundColor = backgroundColor;
}
```
最后 我们需要调节透明度的页面
在滑动过程中 调节颜色的透明度 就OK拉
```
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UIColor * color = [UIColor colorWithHexString:@"#fa2245"];
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY > 50) {
CGFloat alpha = MIN(1, 1 - ((50 + 64 - offsetY) / 64));
[self.navigationController.navigationBar lt_setBackgroundColor:[color colorWithAlphaComponent:alpha]];
} else {
[self.navigationController.navigationBar lt_setBackgroundColor:[color colorWithAlphaComponent:0]];
}
}
```
#2旋转处理
一半我我们的app不支持旋转的话 就会关掉下面这两个向选项
![](http://upload-images.jianshu.io/upload_images/1106106-fdd231479a44cc5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
来禁止横屏。
但如果我们的app部分页面支持横屏,部分不支持的话就要做一些处理了
我们不需要对每一个VC都进行处理,只要对最外层的容器类VC重写一些方法。
例如我的所有VC都是由tabbar管理的
我会在tabbar的子类中
```
#pragma mark -- Orientation
//如果目前是竖屏 则不支持旋转,如果现在是横屏 则支持旋转
- (BOOL)shouldAutorotate{
return !([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
```
在某些需要旋转的VC继续重写这几个方法
```
//因为是个别页面是横屏 其他页面都是竖屏,这里可以直接返回YES
//如果有特殊情况 也可以判断返回
#pragma mark -- orientation
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight;
}
```
这样即使是横屏状态打开app 也会自动切换成竖屏。不过如果你在tabbar 在加一些东西的话可能会出现问题。
举个例子
![2362FE5F-DDC4-48EA-951B-FBA57DB00A43.png](http://upload-images.jianshu.io/upload_images/1106106-9b578dcea13190d7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
中间这个 取 的按钮 ,按照系统的是无法实现的。又不想重新自定义tabbar。这时候我就在tabbar中中间加上一个Button.来模拟tabbar item 的功能。
这时候 从横屏 进来之后,就会出现问题,因为bar 上是无法添加约束的,所以只能用frame 布局。 但是横竖屏的宽高大小 是相反的0.0
这时候就需要判断了
```
//可以用宽高大小判断 也可以用上面的设备方向判断
CGFloat width = kScreenHeight>kScreenWidth? kScreenWidth: kScreenHeight;
UIButton *getGoodsButton = [[UIButton alloc] initWithFrame:CGRectMake(width/2 - kButtonWidth/2, 0, kButtonWidth, kButtonHeight)];
```
这只是一盒小例子,总之 处理屏幕旋转的时候要注意布局。尤其是没有约束控制的布局
#3扫描
3.1淘宝的扫描效果(盗的图QAQ)
![2BA14FEB-7E6F-499E-827A-9FC2E02E5B61.png](http://upload-images.jianshu.io/upload_images/1106106-13b2f05677f71aec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这个看起来很炫酷,其实就是一个网格的图片上下移动,把多余的部分Mask 掉就可以了。
```
//放个可视面积差不多大小的view,将imageview 加上去
UIView *tempView = [[UIView alloc] initWithFrame:VISIBLERECT];
//重点
tempView.layer.masksToBounds = YES;
[self.view addSubview:tempView];
_scanningImageView = [[UIImageView alloc] init];
[tempView addSubview:_scanningImageView];
_scanningImageView.image = [UIImage imageNamed:@"扫描网格"];
```
上下的移动的代码就不贴,思路知道了。做起来很简单
######3.2 unsupported type found
在设置`AVCaptureMetadataOutput ` 的`metadataObjectTypes`时候,一定要注意设置在载入session 之后不然就会出现 特别迷惑人的错误
![E9F4D45F-0FA8-426D-AA5C-11A7FEB11253.png](http://upload-images.jianshu.io/upload_images/1106106-837757e6464675aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我纳闷了好久 啊 。。。。。要记得 前后 顺序
```
[_session addInput:_input];
[_session addOutput:_output];
_output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
```
######3.3 坐标转换
我们知道扫描的可视范围rectOfInterest坐标系统 和 我们 UIKit用的坐标系统不一样,我们在设置这个坐标的时候有两种做法
1,根据 两种坐标系 自己换算去 其实就是将屏幕左翻转 (x,y轴对调)
CGRectMake(y的起点/屏幕的高,x的起点/屏幕的宽,扫描的区域的高/屏幕的高,扫描的区域的宽/屏幕的宽)
2,通过系统的方法换算
在通过系统的方法换算的时候,出现了小问题
```
- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates NS_AVAILABLE_IOS(7_0);
```
在使用这个方法换算的时候始终是没有作用的,
其实方法是没错,但是调用的时机很重要
当我们收到`AVCaptureInputPortFormatDescriptionDidChangeNotification`通知的时候在进行左边转换就没有问题了
具体原因,我也没有找到。希望大神指点
```
[[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
_output.rectOfInterest = [_scanView metadataOutputRectOfInterestForRect:VISIBLERECT];
}];
```
#4UIButton
我们都知道button中有2个子控件 一个imageView 一个label。默认imageview 在左label 在右。我们控制好这几个控件 能满足好多需求
首先 button 是UIControl的子类
UIControl有控制content位置的属性
```
typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) {
UIControlContentVerticalAlignmentCenter = 0,
UIControlContentVerticalAlignmentTop = 1,
UIControlContentVerticalAlignmentBottom = 2,
UIControlContentVerticalAlignmentFill = 3,
};
typedef NS_ENUM(NSInteger, UIControlContentHorizontalAlignment) {
UIControlContentHorizontalAlignmentCenter = 0,
UIControlContentHorizontalAlignmentLeft = 1,
UIControlContentHorizontalAlignmentRight = 2,
UIControlContentHorizontalAlignmentFill = 3,
};
@property(nonatomic) UIControlContentVerticalAlignment contentVerticalAlignment; // how to position content vertically inside control. default is center
@property(nonatomic) UIControlContentHorizontalAlignment contentHorizontalAlignment; // how to position content hozontally inside control. default is center
```
只要我们合理的运用 content(imageview 和 label) 可以在button 内部的16个位置上。
但有时这个东西满足不了我们的需求
看个例子
![FE0CDCAA-451F-4ACF-97BA-74694BA7548B.png](http://upload-images.jianshu.io/upload_images/1106106-d9752444e829622c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这种图片在右边 文字在左边,就令我们很蛋疼。看到这个需求有的童鞋可能就会自定义一个视图类了,其实不需要button的属性就可以帮助我们解决
```
@property(nonatomic) UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
```
可以设置图片文字的偏移量,不过此处有坑
```
#import "UIButton+Common.h"
@implementation UIButton (Common)
- (void)reverseImageAndTitle {
CGRect imageFrame = self.imageView.frame;
CGRect titleFrame = self.titleLabel.frame;
self.titleEdgeInsets = UIEdgeInsetsMake(0, -imageFrame.size.width *2, 0,0 );
self.imageEdgeInsets = UIEdgeInsetsMake(0,0, 0, -titleFrame.size.width*2);
}
@end
```
这边单位是px 不是pt 整个人都蒙了有木有。
不过还好发现了
#5UITextField
![C04C5E25-F123-4372-A707-B14ACD5D7EAD.png](http://upload-images.jianshu.io/upload_images/1106106-2beb7f5591482bab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
设置左边图片 sb 上面没有对应的属性。。。庆幸的在textfield中还是有对应的属性 不过切图的时候记得要来拿左边的空白一起切下来
```
@property(nullable, nonatomic,strong) UIView *leftView; // e.g. magnifying glass
@property(nonatomic) UITextFieldViewMode leftViewMode; // sets when the left view shows up. default is UITextFieldViewModeNever
```
#6.tableView collectionView
######1.圆角 始终是个争论的话题了。
各种花式的解决办法。
其实我认为,如果固定的页面 如果只有个别原角,无需优化
如果想tableview 这样大量重复的圆角,其实只要做设置一次缓存即可。开启光栅化,可解决性能问题。
什么 离屏渲染 CPU GPU线程切换消耗大量性能,我只做一次还不行meQAQ
```
//rasterize
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
```
不要各种贝塞尔 各种mask 简单点好。
######2.对于selected
tableview 和 collectionview 都是支持单选的,只要重写 selected方法就行,系统在你创建cell类的时候也预留了这个方法。
不过要选择默认的selected 设置cell.seleted 是不行的
```
//collectionView
- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition;
```
seleted 可以完美的实现默认选中 单选的效果。
对于对选 智能自定义了
######3section 展开
只要用数组记录每个section 需要展示的cell 的数量 刷新view 就可以了
#7VC设置背景图片
没有必要再放在一个imageview上面 直接在VC的view.layer上画就可以了
```
self.view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"beijing"].CGImage)
```