有段時間沒寫東西了粤蝎,最近都是機(jī)械搭項目。趁著后臺袋马,出接口的這幾天初澎。
咱也定一個小目標(biāo),比如說-----
算了虑凛,不吹牛逼了碑宴,怕被打死。
最近剛做一個項目卧檐,準(zhǔn)備在項目中穿插著寫一點(diǎn)東西吧墓懂。
1雜碎的知識點(diǎn)
2講一下項目大體的架構(gòu),分析一下項目用的三方的一些問題和選擇
3講一下霉囚,我這個菜雞對項目網(wǎng)絡(luò)層以及數(shù)據(jù)存儲層的設(shè)計以及分析
項目沒做完之前捕仔,我會不斷更新雜碎的知識點(diǎn)這篇,等項目over.和你們好好嘮嘮項目整體。
好了不Xx了
1.1 UINavigationController
導(dǎo)航欄控制器基本是項目必備啊榜跌,所以我們UI大姐們也是想著花樣搞這個東西闪唆。我們做這方面開發(fā)的都是到NVC(碼字 有點(diǎn)煩,后面都這樣簡稱了)钓葫,基本構(gòu)造做開發(fā)的都是知道悄蕾,是一個容器類 下面的VCs都是放在 stack中的。所以說础浮,在stack里面那么多VC都是公用一個navigationBar啊帆调。UI大姐就喜歡一個VC 擁有一個不同的bar....欲哭無淚。
現(xiàn)在按按修改程度豆同,一點(diǎn)一點(diǎn)寫番刊。
1.1.1bar 上的返回按鈕太丑了 換掉0.0
~~~~~第二個方法就是整個替換掉leftItem,當(dāng)然隨之而來的問題就是要實(shí)現(xiàn)點(diǎn)擊返回功能和左滑返回功能。
因為要實(shí)現(xiàn)左滑 最好還是重寫一個NVC子類 而不是用appearce
```
//遵循手勢的協(xié)議
@interface IGONavigationController ()<UIGestureRecognizerDelegate>
- (void)viewDidLoad{
[super viewDidLoad];
self.interactivePopGestureRecognizer.delegate = self;
}
//替換掉各個頁面的返回按鈕
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//設(shè)置返回按鈕
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; // 默認(rèn)為支持右滑反回
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 不然就會出現(xiàn)空stack的問題鸭廷,從而導(dǎo)致各種問題枣抱。
######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;
}
```
這種做法,也就效果勉強(qiáng)出來了维雇,不過會帶來一系列的問題
·····1 左滑 返回的時候NVC 無法分清那個VC 有Bar .出現(xiàn)顯示問題
·····2當(dāng)2個VC對應(yīng) statusBar 的字體顏色不一樣的時候淤刃。上面這種做法,雖然隱藏了Bar 但在VC中重寫`preferredStatusBarStyle`依舊沒有作用的吱型。NVC很難分清其中的界限逸贾。
正確的做法應(yīng)該是
這種修改Bar 的工作還得交給代理去做,畢竟出現(xiàn)的意外情況津滞,蘋果的大牛們都考慮到了铝侵,代碼要寫在改寫的地方。
因為我子類化得NVC用的地方很多触徐,所以對于Bar的代理 我基本會放在rootVC中
下面的代碼 就是在 SearchViewController和SearchResultController中隱藏了Bar
在ProductDetailViewController中對Bar的樣式 進(jìn)行了修改咪鲜。
```
//在rootVC 中 遵循NVC代理 UINavigationControllerDelegate
//隱藏導(dǎo)航欄
- (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:@"導(dǎo)航欄"] forBarMetrics:UIBarMetricsDefault];
}
}
```
這樣 在左滑的時候消除了顯示問題,以及在在隱藏了Bar的VC中無法控制statusBar的狀態(tài)顏色問題撞鹉。
######1.1.3 每個bar完全不同
童鞋 全隱藏 自定義吧QAQ
#1.2UINavigationBar
大家現(xiàn)在都是最低支持7以上了疟丙,太老的東西就不涉及了颖侄。
bar 有2個煩人的地方就是偏移量和透明度了
######1.2.1偏移量
我們知道默認(rèn)`translucent = YES`。就是Bar 是有透明度的享郊。
在有透明度的情況下览祖,系統(tǒng)默認(rèn)`automaticallyAdjustsScrollViewInsets`屬性是YES就是對于scrollerview的子類會默認(rèn)content偏移64個單位。這樣做的目的炊琉,既然Bar都是透明的了展蒂,系統(tǒng)就覺得你的scrollerView一定在會在(0,0)點(diǎn)苔咪,content偏移一個64個單位锰悼。在你滑動的時候,隱藏在Bar下面的Content會有一個模糊顯示的效果团赏。QAQ
如果你不想要這個偏移量
1 設(shè)置translucent = NO,既然Bar不透明松捉。自然不需要偏移量嘍
2 關(guān)閉這個偏移量,automaticallyAdjustsScrollViewInsets = NO
提一下像tableView馆里,在storyboard 設(shè)置上下左右的約束。結(jié)果cell上面多了一塊空白可柿。就是這個問題鸠踪。
######1.2.2透明度
這種需求現(xiàn)在太多了,尤其都要做成淘寶那樣的詳情頁复斥,煩的一腿∮埽現(xiàn)在我就在分析一下。
首先如果想滑動控制Bar的透明度目锭,translucent 屬性一定要設(shè)置成YES(默認(rèn))
當(dāng)Bar 被設(shè)置成透明狀態(tài)的時候
![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是設(shè)置半透明毛玻璃效果的view(當(dāng)然只在透明屬性為YES 或者 沒有背景圖片的時候才會出現(xiàn))
首先我們要去掉層特殊的視覺圖層 利用設(shè)置BarBackground圖片
```
- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
```
千萬不要用translucent = NO 這種方法來去掉視覺圖層评汰,都設(shè)置不透明了,那我們還玩按什么是吧痢虹。被去。。
這里注意的是我們設(shè)置的barTintColor 以及 background 都是對_UINavigationBarground這個Imageview來進(jìn)行操作的奖唯。
這時候 我們就剩下Bar 和 BarBackground兩層了
首先我們來說下Bar上面設(shè)置顏色的3個方法
```
//背景顏色 繼承父類的 不過這個是設(shè)置bar 的背景顏色
@property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is nil. Can be useful with the appearance proxy on custom UIView subclasses.
//這個是設(shè)置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 確實(shí)64.惨缆。
這就是 為什么你設(shè)置了bar你顏色 或者 背景圖片 。為什么status Bar 的背景也會跟著改變哦丰捷。
其實(shí)Bar 本身的顏色幾乎很少設(shè)置坯墨,因為現(xiàn)在都要2個bar 一致才符合主流嘛
怎么改變barBackgroundImageView的背景顏色,又成了一個問題病往。因為剛才為了去掉視覺圖層捣染,我們給barBackgroundImageView 賦值了一個空圖片。問題出來了停巷,有了圖片屬性之后耍攘,給barBackgroundImageView設(shè)置背景顏色就沒有用了榕栏,現(xiàn)在主流的做法是
查一個view進(jìn)去 去調(diào)節(jié)這個 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;
}
```
最后 我們需要調(diào)節(jié)透明度的頁面
在滑動過程中 調(diào)節(jié)顏色的透明度 就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旋轉(zhuǎn)處理
一半我我們的app不支持旋轉(zhuǎn)的話 就會關(guān)掉下面這兩個向選項
![](http://upload-images.jianshu.io/upload_images/1106106-fdd231479a44cc5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
來禁止橫屏。
但如果我們的app部分頁面支持橫屏少漆,部分不支持的話就要做一些處理了
我們不需要對每一個VC都進(jìn)行處理臼膏,只要對最外層的容器類VC重寫一些方法。
例如我的所有VC都是由tabbar管理的
我會在tabbar的子類中
```
#pragma mark -- Orientation
//如果目前是豎屏 則不支持旋轉(zhuǎn)示损,如果現(xiàn)在是橫屏 則支持旋轉(zhuǎn)
- (BOOL)shouldAutorotate{
return !([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait);
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
```
在某些需要旋轉(zhuǎn)的VC繼續(xù)重寫這幾個方法
```
//因為是個別頁面是橫屏 其他頁面都是豎屏渗磅,這里可以直接返回YES
//如果有特殊情況 也可以判斷返回
#pragma mark -- orientation
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight;
}
```
這樣即使是橫屏狀態(tài)打開app 也會自動切換成豎屏。不過如果你在tabbar 在加一些東西的話可能會出現(xiàn)問題检访。
舉個例子
![2362FE5F-DDC4-48EA-951B-FBA57DB00A43.png](http://upload-images.jianshu.io/upload_images/1106106-9b578dcea13190d7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
中間這個 取 的按鈕 始鱼,按照系統(tǒng)的是無法實(shí)現(xiàn)的。又不想重新自定義tabbar脆贵。這時候我就在tabbar中中間加上一個Button.來模擬tabbar item 的功能医清。
這時候 從橫屏 進(jìn)來之后,就會出現(xiàn)問題卖氨,因為bar 上是無法添加約束的会烙,所以只能用frame 布局。 但是橫豎屏的寬高大小 是相反的0.0
這時候就需要判斷了
```
//可以用寬高大小判斷 也可以用上面的設(shè)備方向判斷
CGFloat width = kScreenHeight>kScreenWidth? kScreenWidth: kScreenHeight;
UIButton *getGoodsButton = [[UIButton alloc] initWithFrame:CGRectMake(width/2 - kButtonWidth/2, 0, kButtonWidth, kButtonHeight)];
```
這只是一盒小例子筒捺,總之 處理屏幕旋轉(zhuǎn)的時候要注意布局柏腻。尤其是沒有約束控制的布局
#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)
這個看起來很炫酷,其實(shí)就是一個網(wǎng)格的圖片上下移動系吭,把多余的部分Mask 掉就可以了五嫂。
```
//放個可視面積差不多大小的view,將imageview 加上去
UIView *tempView = [[UIView alloc] initWithFrame:VISIBLERECT];
//重點(diǎn)
tempView.layer.masksToBounds = YES;
[self.view addSubview:tempView];
_scanningImageView = [[UIImageView alloc] init];
[tempView addSubview:_scanningImageView];
_scanningImageView.image = [UIImage imageNamed:@"掃描網(wǎng)格"];
```
上下的移動的代碼就不貼肯尺,思路知道了沃缘。做起來很簡單
######3.2 unsupported type found
在設(shè)置`AVCaptureMetadataOutput ` 的`metadataObjectTypes`時候,一定要注意設(shè)置在載入session 之后不然就會出現(xiàn) 特別迷惑人的錯誤
![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 坐標(biāo)轉(zhuǎn)換
我們知道掃描的可視范圍rectOfInterest坐標(biāo)系統(tǒng) 和 我們 UIKit用的坐標(biāo)系統(tǒng)不一樣,我們在設(shè)置這個坐標(biāo)的時候有兩種做法
1寨昙,根據(jù) 兩種坐標(biāo)系 自己換算去 其實(shí)就是將屏幕左翻轉(zhuǎn) (x,y軸對調(diào))
CGRectMake(y的起點(diǎn)/屏幕的高讥巡,x的起點(diǎn)/屏幕的寬,掃描的區(qū)域的高/屏幕的高舔哪,掃描的區(qū)域的寬/屏幕的寬)
2欢顷,通過系統(tǒng)的方法換算
在通過系統(tǒng)的方法換算的時候,出現(xiàn)了小問題
```
- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates NS_AVAILABLE_IOS(7_0);
```
在使用這個方法換算的時候始終是沒有作用的捉蚤,
其實(shí)方法是沒錯抬驴,但是調(diào)用的時機(jī)很重要
當(dāng)我們收到`AVCaptureInputPortFormatDescriptionDidChangeNotification`通知的時候在進(jìn)行左邊轉(zhuǎn)換就沒有問題了
具體原因炼七,我也沒有找到。希望大神指點(diǎn)
```
[[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
_output.rectOfInterest = [_scanView metadataOutputRectOfInterestForRect:VISIBLERECT];
}];
```
#4UIButton
我們都知道button中有2個子控件 一個imageView 一個label布持。默認(rèn)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
```
只要我們合理的運(yùn)用 content(imageview 和 label) 可以在button 內(nèi)部的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)
這種圖片在右邊 文字在左邊题暖,就令我們很蛋疼按傅。看到這個需求有的童鞋可能就會自定義一個視圖類了胧卤,其實(shí)不需要button的屬性就可以幫助我們解決
```
@property(nonatomic) UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
```
可以設(shè)置圖片文字的偏移量唯绍,不過此處有坑
```
#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 整個人都蒙了有木有。
不過還好發(fā)現(xiàn)了
#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)
設(shè)置左邊圖片 sb 上面沒有對應(yīng)的屬性。。么伯。慶幸的在textfield中還是有對應(yīng)的屬性 不過切圖的時候記得要來拿左邊的空白一起切下來
```
@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.圓角 始終是個爭論的話題了。
各種花式的解決辦法绝骚。
其實(shí)我認(rèn)為,如果固定的頁面 如果只有個別原角祠够,無需優(yōu)化
如果想tableview 這樣大量重復(fù)的圓角皮壁,其實(shí)只要做設(shè)置一次緩存即可。開啟光柵化哪审,可解決性能問題。
什么 離屏渲染 CPU GPU線程切換消耗大量性能虑瀑,我只做一次還不行meQAQ
```
//rasterize
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
```
不要各種貝塞爾 各種mask 簡單點(diǎn)好湿滓。
######2.對于selected
tableview 和 collectionview 都是支持單選的,只要重寫 selected方法就行舌狗,系統(tǒng)在你創(chuàng)建cell類的時候也預(yù)留了這個方法叽奥。
不過要選擇默認(rèn)的selected 設(shè)置cell.seleted 是不行的
```
//collectionView
- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition;
```
seleted 可以完美的實(shí)現(xiàn)默認(rèn)選中 單選的效果。
對于對選 智能自定義了
######3section 展開
只要用數(shù)組記錄每個section 需要展示的cell 的數(shù)量 刷新view 就可以了
#7VC設(shè)置背景圖片
沒有必要再放在一個imageview上面 直接在VC的view.layer上畫就可以了
```
self.view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"beijing"].CGImage)
```