1.1 UINavigationController
导航栏控制器基本是项目必备啊,所以我们UI大姐们也是想着花样搞这个东西。我们做这方面开发的都是到NVC(码字 有点烦,后面都这样简称了),基本构造做开发的都是知道,是一个容器类 下面的VCs都是放在 stack中的。所以说,在stack里面那么多VC都是公用一个navigationBar啊。UI大姐就喜欢一个VC 拥有一个不同的bar....欲哭无泪。
现在按按修改程度,一点一点写。
1.1.1bar 上的返回按钮太丑了
~~第一种方法就是原本backItem不动,将文字移到看不见的地方,并且更换返回图片。个人感觉不是很好
~第二个方法就是整个替换掉leftItem,当然随之而来的问题就是要实现点击返回功能和左滑返回功能。
因为要实现左滑 最好还是重写一个NVC子类 而不是用appearce
//遵循手势的协议@interfaceIGONavigationController()- (void)viewDidLoad{ [superviewDidLoad];self.interactivePopGestureRecognizer.delegate=self;}//替换掉各个页面的返回按钮- (void)pushViewController:(UIViewController*)viewController animated:(BOOL)animated{//设置返回按钮viewController.navigationItem.leftBarButtonItem= [[UIBarButtonItemalloc] initWithImage:[UIImageimageNamed:@"fanhui"] style:UIBarButtonItemStyleDonetarget:selfaction:@selector(popViewControllerAnimated:)]; [superpushViewController:viewController animated:animated];}//右滑返回的处理- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer{BOOLok =YES;// 默认为支持右滑反回if([self.topViewControllerisKindOfClass:[RootViewController class]] || [self.topViewControllerisKindOfClass:[Root1ViewController class]] || [self.topViewControllerisKindOfClass:[Root2ViewController class]] || [self.topViewControllerisKindOfClass:[Root3ViewController class]]) {returnNO; }returnok;}
要注意的是 开起左滑 返回 移动去去掉 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{// 隐藏BarBOOLisShowHomePage = [viewController isKindOfClass:[SearchViewController class]] || [viewController isKindOfClass:[SearchResultController class]]; [self.navigationControllersetNavigationBarHidden:isShowHomePage animated:YES];UIImage*shadowImage =self.navigationController.navigationBar.shadowImage;UINavigationBar*bar =self.navigationController.navigationBar;if([viewController isKindOfClass:[ProductDetailViewController class]]) { bar.shadowImage= [UIImagenew]; [bar lt_setBackgroundColor:[UIColorclearColor]]; }else{ bar.shadowImage= shadowImage; [bar setBackgroundImage:[UIImageimageNamed:@"导航栏"] 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
还是这张图 _UIBackdropView 这个view是设置半透明毛玻璃效果的view(当然只在透明属性为YES 或者 没有背景图片的时候才会出现)
首先我们要去掉层特殊的视觉图层 利用设置BarBackground图片
- (void)setBackgroundImage:(nullableUIImage*)backgroundImage forBarMetrics:(UIBarMetrics)barMetricsNS_AVAILABLE_IOS(5_0)UI_APPEARANCE_SELECTOR;
千万不要用translucent = NO 这种方法来去掉视觉图层,都设置不透明了,那我们还玩按什么是吧。。。
这里注意的是我们设置的barTintColor 以及 background 都是对_UINavigationBarground这个Imageview来进行操作的。
这时候 我们就剩下Bar 和 BarBackground两层了
首先我们来说下Bar上面设置颜色的3个方法
//背景颜色 继承父类的 不过这个是设置bar 的背景颜色@property(nullable,nonatomic,copy)UIColor*backgroundColorUI_APPEARANCE_SELECTOR;// default is nil. Can be useful with the appearance proxy on custom UIView subclasses.//这个是设置bar 上面的那层 barBackground imaeView的颜色@property(nullable,nonatomic,strong)UIColor*barTintColorNS_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{returnobjc_getAssociatedObject(self, &backView);}- (void)backView:(UIView*)backView{ objc_setAssociatedObject(self, &backView, backView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (void)iGo_setBackgroundColor:(UIColor*)backgroundColor{if(!self.backView) { [selfsetBackgroundImage:[UIImagenew] forBarMetrics:UIBarMetricsDefault];self.backView= [[UIViewalloc] initWithFrame:CGRectMake(0, -20,CGRectGetWidth(self.bounds),CGRectGetHeight(self.bounds) +20)]; [selfinsertSubview:self.backViewatIndex:0]; }self.backView.backgroundColor= backgroundColor;}
最后 我们需要调节透明度的页面
在滑动过程中 调节颜色的透明度 就OK拉
- (void)scrollViewDidScroll:(UIScrollView*)scrollView{UIColor* color = [UIColorcolorWithHexString:@"#fa2245"];CGFloatoffsetY = scrollView.contentOffset.y;if(offsetY >50) {CGFloatalpha = MIN(1,1- ((50+64- offsetY) /64)); [self.navigationController.navigationBarlt_setBackgroundColor:[color colorWithAlphaComponent:alpha]]; }else{ [self.navigationController.navigationBarlt_setBackgroundColor:[color colorWithAlphaComponent:0]]; }}
2旋转处理
一半我我们的app不支持旋转的话 就会关掉下面这两个向选项
来禁止横屏。
但如果我们的app部分页面支持横屏,部分不支持的话就要做一些处理了
我们不需要对每一个VC都进行处理,只要对最外层的容器类VC重写一些方法。
例如我的所有VC都是由tabbar管理的
我会在tabbar的子类中
#pragma mark -- Orientation//如果目前是竖屏 则不支持旋转,如果现在是横屏 则支持旋转- (BOOL)shouldAutorotate{return!([UIApplicationsharedApplication].statusBarOrientation==UIInterfaceOrientationPortrait);}- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{returnUIInterfaceOrientationPortrait;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations {returnUIInterfaceOrientationMaskPortrait;}
在某些需要旋转的VC继续重写这几个方法
//因为是个别页面是横屏 其他页面都是竖屏,这里可以直接返回YES//如果有特殊情况 也可以判断返回#pragma mark -- orientation- (BOOL)shouldAutorotate {returnYES;}- (UIInterfaceOrientationMask)supportedInterfaceOrientations {returnUIInterfaceOrientationMaskLandscapeRight;}
这样即使是横屏状态打开app 也会自动切换成竖屏。不过如果你在tabbar 在加一些东西的话可能会出现问题。
中间这个 取 的按钮 ,按照系统的是无法实现的。又不想重新自定义tabbar。这时候我就在tabbar中中间加上一个Button.来模拟tabbar item 的功能。
这时候 从横屏 进来之后,就会出现问题,因为bar 上是无法添加约束的,所以只能用frame 布局。 但是横竖屏的宽高大小 是相反的0.0
这时候就需要判断了
//可以用宽高大小判断 也可以用上面的设备方向判断CGFloatwidth = kScreenHeight>kScreenWidth? kScreenWidth: kScreenHeight;UIButton*getGoodsButton = [[UIButtonalloc] initWithFrame:CGRectMake(width/2- kButtonWidth/2,0, kButtonWidth, kButtonHeight)];
这只是一盒小例子,总之 处理屏幕旋转的时候要注意布局。尤其是没有约束控制的布局
3扫描
3.1淘宝的扫描效果
这个看起来很炫酷,其实就是一个网格的图片上下移动,把多余的部分Mask 掉就可以了。
//放个可视面积差不多大小的view,将imageview 加上去UIView*tempView = [[UIViewalloc] initWithFrame:VISIBLERECT];//重点tempView.layer.masksToBounds=YES; [self.viewaddSubview:tempView]; _scanningImageView = [[UIImageViewalloc] init]; [tempView addSubview:_scanningImageView]; _scanningImageView.image= [UIImageimageNamed:@"扫描网格"];
上下的移动的代码就不贴,思路知道了。做起来很简单
3.2 unsupported type found
在设置AVCaptureMetadataOutput的metadataObjectTypes时候,一定要注意设置在载入session 之后不然就会出现 特别迷惑人的错误
E9F4D45F-0FA8-426D-AA5C-11A7FEB11253.png
我纳闷了好久 啊 。。。。。要记得 前后 顺序
[_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)rectInLayerCoordinatesNS_AVAILABLE_IOS(7_0);
在使用这个方法换算的时候始终是没有作用的,
其实方法是没错,但是调用的时机很重要
当我们收到AVCaptureInputPortFormatDescriptionDidChangeNotification通知的时候在进行左边转换就没有问题了
具体原因,我也没有找到。希望大神指点
[[NSNotificationCenterdefaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotificationobject:nilqueue:[NSOperationQueuecurrentQueue] usingBlock:^(NSNotification* _Nonnull note) { _output.rectOfInterest= [_scanView metadataOutputRectOfInterestForRect:VISIBLERECT]; }];
4UIButton
我们都知道button中有2个子控件 一个imageView 一个label。默认imageview 在左label 在右。我们控制好这几个控件 能满足好多需求
首先 button 是UIControl的子类
UIControl有控制content位置的属性
typedefNS_ENUM(NSInteger,UIControlContentVerticalAlignment) {UIControlContentVerticalAlignmentCenter=0,UIControlContentVerticalAlignmentTop=1,UIControlContentVerticalAlignmentBottom=2,UIControlContentVerticalAlignmentFill=3,};typedefNS_ENUM(NSInteger,UIControlContentHorizontalAlignment) {UIControlContentHorizontalAlignmentCenter=0,UIControlContentHorizontalAlignmentLeft=1,UIControlContentHorizontalAlignmentRight=2,UIControlContentHorizontalAlignmentFill=3,};@property(nonatomic)UIControlContentVerticalAlignmentcontentVerticalAlignment;// how to position content vertically inside control. default is center@property(nonatomic)UIControlContentHorizontalAlignmentcontentHorizontalAlignment;// how to position content hozontally inside control. default is center
只要我们合理的运用 content(imageview 和 label) 可以在button 内部的16个位置上。
但有时这个东西满足不了我们的需求
看个例子
FE0CDCAA-451F-4ACF-97BA-74694BA7548B.png
这种图片在右边 文字在左边,就令我们很蛋疼。看到这个需求有的童鞋可能就会自定义一个视图类了,其实不需要button的属性就可以帮助我们解决
@property(nonatomic)UIEdgeInsetscontentEdgeInsetsUI_APPEARANCE_SELECTOR;// default is UIEdgeInsetsZero@property(nonatomic)UIEdgeInsetstitleEdgeInsets;// default is UIEdgeInsetsZero
可以设置图片文字的偏移量,不过此处有坑
#import"UIButton+Common.h"@implementationUIButton(Common)- (void)reverseImageAndTitle {CGRectimageFrame =self.imageView.frame;CGRecttitleFrame =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
设置左边图片 sb 上面没有对应的属性。。。庆幸的在textfield中还是有对应的属性 不过切图的时候记得要来拿左边的空白一起切下来
@property(nullable,nonatomic,strong)UIView*leftView;// e.g. magnifying glass@property(nonatomic)UITextFieldViewModeleftViewMode;// sets when the left view shows up. default is UITextFieldViewModeNever
6.tableView collectionView
1.圆角 始终是个争论的话题了。
各种花式的解决办法。
其实我认为,如果固定的页面 如果只有个别原角,无需优化
如果想tableview 这样大量重复的圆角,其实只要做设置一次缓存即可。开启光栅化,可解决性能问题。
什么 离屏渲染 CPU GPU线程切换消耗大量性能,我只做一次还不行meQAQ
//rasterizecell.layer.shouldRasterize=YES; cell.layer.rasterizationScale= [UIScreenmainScreen].scale;
不要各种贝塞尔 各种mask 简单点好。
2.对于selected
tableview 和 collectionview 都是支持单选的,只要重写 selected方法就行,系统在你创建cell类的时候也预留了这个方法。
不过要选择默认的selected 设置cell.seleted 是不行的
//collectionView- (void)selectItemAtIndexPath:(nullableNSIndexPath*)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition;
seleted 可以完美的实现默认选中 单选的效果。
对于对选 智能自定义了
3section 展开
只要用数组记录每个section 需要展示的cell 的数量 刷新view 就可以了
7VC设置背景图片
没有必要再放在一个imageview上面 直接在VC的view.layer上画就可以了
self.view.layer.contents= (__bridgeid_Nullable)([UIImageimageNamed:@"beijing"].CGImage.