標籤:
網易新聞首頁類似的介面簡直太常見了,需求不同自然做出來的效果不同了,之前
用ScrollView寫過一個控制器的封裝,但是這雷根本沒有考慮到控制器的複用以及預加
載機制,如果沒考慮複用的話當介面爆炸的時候估計你的App會很卡,例如半塘這樣
的,我抓包發現貌似會預先載入當前介面後三個介面,讓使用者滑動的時候能第一時間看到
資料,這樣的機制蠻不錯的,今天來介紹個能複用的架構,順帶介紹個另一個高斯模糊
的Catagory。
這裡的三個介面分別是最普通的控制器,webview以及雙TableView
該架構有兩種用法,第一種直接建立一個ViewController繼承它,第二個就是在控制器寫成屬性
這裡我們介紹第二種
@property (nonatomic,strong)MXSegmentedPager *segmentedPager;//!< MX架構
第一步(建立和準備)
用你的終端建立podfile檔案,在裡面寫上你需要的庫,然後就開搞了
platform:ios,‘7.0‘
target‘imageBlur‘do
pod‘MXSegmentedPager‘
pod‘SDWebImage‘
pod‘Masonry‘
end
既然準備的話就準備一個可以展開的頭部,來展示展開效果以及高斯模糊的效果
屬性拖出來以備後續操作
第二步(初始化架構MX)
我們展示三個控制器,按如下,分別給出幾個屬性
超級無敵詳細的備忘介紹如何初始化架構
// 初始化MX架構控制器,頭部和選擇欄- (void)initMX{ // 頭部 self.segmentedPager = [[MXSegmentedPager alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; self.segmentedPager.parallaxHeader.view = self.headView; // 注意這裡載入平行頭部 // MXParallaxHeaderModeCenter MXParallaxHeaderModeCenter MXParallaxHeaderModeTop MXParallaxHeaderModeBottom四個,大家可以自己測試 self.segmentedPager.parallaxHeader.mode = MXParallaxHeaderModeFill; // 平行頭部填充模式 self.segmentedPager.parallaxHeader.height = 240; // 頭部高度 self.segmentedPager.parallaxHeader.minimumHeight = 64; // 頭部最小高度 // 選擇欄控制器屬性 self.segmentedPager.segmentedControl.borderWidth = 1.0; // 邊框寬度 self.segmentedPager.segmentedControl.borderColor = [UIColor redColor]; // 邊框顏色 self.segmentedPager.segmentedControl.frame = CGRectMake(0, 0, self.view.bounds.size.width, 44); // frame self.segmentedPager.segmentedControl.segmentEdgeInset = UIEdgeInsetsMake(0, 10, 0, 10);// 間距 self.segmentedPager.segmentedControl.selectionIndicatorHeight = 0;// 底部是否需要橫條指標,0的話就沒有了, // 底部指標的寬度是否根據內容 self.segmentedPager.segmentedControl.selectionStyle = HMSegmentedControlSelectionStyleTextWidthStripe; //HMSegmentedControlSelectionIndicatorLocationNone 不需要底部滑動指標 self.segmentedPager.segmentedControl.selectionIndicatorLocation = HMSegmentedControlSelectionIndicatorLocationNone; self.segmentedPager.segmentedControl.verticalDividerEnabled = NO;// 不可以垂直滾動 // fix的枚舉說明寬度是適應螢幕的,不會根據字型 HMSegmentedControlSegmentWidthStyleDynamic則是字型多大就多寬 self.segmentedPager.segmentedControl.segmentWidthStyle = HMSegmentedControlSegmentWidthStyleFixed; // 預設狀態的字型 self.segmentedPager.segmentedControl.titleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1], NSFontAttributeName : [UIFont systemFontOfSize:14]}; // 選擇狀態下的字型 self.segmentedPager.segmentedControl.selectedTitleTextAttributes = @{NSForegroundColorAttributeName : [UIColor colorWithRed:255/255.0 green:174/255.0 blue:1 alpha:1], NSFontAttributeName : [UIFont systemFontOfSize:18]}; self.segmentedPager.segmentedControlEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); self.segmentedPager.delegate = self; self.segmentedPager.dataSource = self; [self.view addSubview:self.segmentedPager]; [self.segmentedPager mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.view.mas_top).with.offset(0); make.left.equalTo(self.view.mas_left); make.bottom.equalTo(self.view.mas_bottom); make.right.equalTo(self.view.mas_right); make.width.equalTo(self.view.mas_width); }];}
第三步(代理方法)
最關鍵的代理方法實現,分別是頭部選擇欄的代理數組以及控制器的數組View,具體的控制器初始化可以在懶載入中
實現
#pragma -mark <MXSegmentedPagerDelegate>- (CGFloat)heightForSegmentedControlInSegmentedPager:(MXSegmentedPager *)segmentedPager{ // 指示欄的高度 return 44.0f;}#pragma -mark <MXSegmentedPagerDataSource>- (NSInteger)numberOfPagesInSegmentedPager:(MXSegmentedPager *)segmentedPager{ // 需要多少個介面 return 3;}- (NSString *)segmentedPager:(MXSegmentedPager *)segmentedPager titleForSectionAtIndex:(NSInteger)index{ // 指示欄的文字數組 return [@[@"控制器介面", @"WKWebView",@"雙TableView"] objectAtIndex:index];}- (UIView *)segmentedPager:(MXSegmentedPager *)segmentedPager viewForPageAtIndex:(NSInteger)index{ // 第一個是控制器的View 第二個是WebView 第三個是自訂的View 這個也是最關鍵的,通過懶載入把對應控制的初始化View載入到架構上面去 return [@[self.firstChildVC.view, self.webView, self.customView] objectAtIndex:index];}
第四步(導覽列消失)
我們在滾動的時候需要讓導覽列也跟著變換,那麼還有個代理方法需要實現
// 滾動整體的時候調用- (void)segmentedPager:(MXSegmentedPager *)segmentedPager didScrollWithParallaxHeader:(MXParallaxHeader *)parallaxHeader{ // 通過拿到滾動的對應的View UIScrollView *scrollView = (UIScrollView *)segmentedPager.subviews[0]; NSLog(@"%lf",scrollView.contentOffset.y); // 計算alpha值 CGFloat headAlpha = (1 - (-(scrollView.contentOffset.y + 64) / 136)) >= 0 ? (1 - (-(scrollView.contentOffset.y + 64) / 136)) : 0; self.headView.alpha = 1 - headAlpha; if (self.headView.alpha == 0) { self.navigationController.navigationBar.hidden = NO; } else { self.navigationController.navigationBar.hidden = YES; }}
第五步(頭部圖片展開以及高斯模糊)
其實上面四個做完基本就差不多了,一個Demo就很簡單的了,那麼我們再加個頭部,展開玩玩,內建的平行頭部,
不玩白不玩
// 載入頭部模糊視圖的圖片和頭像- (void)initHeadView{ // 圓形頭像無需模糊 self.nickImageView.layer.cornerRadius = 25.0f; self.nickImageView.clipsToBounds = YES; self.nickImageView.layer.borderColor = [[UIColor whiteColor] CGColor]; self.nickImageView.layer.borderWidth = 2.0f; __weak typeof(self)weakSelf = self; [self.nickImageView sd_setImageWithURL:[NSURL URLWithString:@"http://twt.img.iwala.net/touxiang/563846208921b.jpg"] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { if (image && cacheType == SDImageCacheTypeNone) { [weakSelf.nickImageView setNeedsDisplay]; weakSelf.nickImageView.alpha = 0; [UIView animateWithDuration:1.0 animations:^{ weakSelf.nickImageView.alpha = 1.0f; }]; } else { weakSelf.nickImageView.alpha = 1.0f; } }]; // 背景做成模糊,方法已經在Demo裡面了,大家可以下載去取,一個方法暴露,簡單就能實現 [self.blurImageView sd_setImageWithURL:[NSURL URLWithString:@"http://twt.img.iwala.net/touxiang/563846208921b.jpg"] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { [self.blurImageView setNeedsDisplay]; // 模糊開始 NSData *imageData = UIImageJPEGRepresentation(image, 0.5); // 模糊介面,參數越接近1越模糊,直接調用他就OK了 UIImage *blurImage = [[UIImage imageWithData:imageData] blurredImage:0.15f]; weakSelf.blurImageView.image = blurImage; if (image && cacheType == SDImageCacheTypeNone) { weakSelf.blurImageView.alpha = 0; [UIView animateWithDuration:1.0 animations:^{ weakSelf.blurImageView.alpha = 1.0f; }]; } else { weakSelf.blurImageView.alpha = 1.0f; } }]; [self.headView layoutSubviews];}
高斯的內部方法,可以拿去直接用
- (UIImage*)blurredImage:(CGFloat)blurAmount{ if (blurAmount < 0.0 || blurAmount > 1.0) { blurAmount = 0.5; } int boxSize = (int)(blurAmount * 40); boxSize = boxSize - (boxSize % 2) + 1; CGImageRef img = self.CGImage; vImage_Buffer inBuffer, outBuffer; vImage_Error error; void *pixelBuffer; CGDataProviderRef inProvider = CGImageGetDataProvider(img); CFDataRef inBitmapData = CGDataProviderCopyData(inProvider); inBuffer.width = CGImageGetWidth(img); inBuffer.height = CGImageGetHeight(img); inBuffer.rowBytes = CGImageGetBytesPerRow(img); inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData); pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img)); outBuffer.data = pixelBuffer; outBuffer.width = CGImageGetWidth(img); outBuffer.height = CGImageGetHeight(img); outBuffer.rowBytes = CGImageGetBytesPerRow(img); error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend); if (!error) { error = vImageBoxConvolve_ARGB8888(&outBuffer, &inBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend); } if (error) {#ifdef DEBUG NSLog(@"%s error: %zd", __PRETTY_FUNCTION__, error);#endif return self; } CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef ctx = CGBitmapContextCreate(outBuffer.data, outBuffer.width, outBuffer.height, 8, outBuffer.rowBytes, colorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast); CGImageRef imageRef = CGBitmapContextCreateImage (ctx); UIImage *returnImage = [UIImage imageWithCGImage:imageRef]; CGContextRelease(ctx); CGColorSpaceRelease(colorSpace); free(pixelBuffer); CFRelease(inBitmapData); CGImageRelease(imageRef); return returnImage;}
用法就介紹到這裡,非常簡單,和TableView的用法差不多,初始化之後直接實現幾個
代理方法一樣,用起來十分習慣,至於內部的複用是如何?的這
裡就不多介紹了,各位有興趣的話可以看下源碼是如何進行複用的
我去,忘了介紹了。這裡還有個雙TableView的展示
我們首先要做的是自訂一個View繼承UIView
@interface MXCustomView () <MXPageProtocol, UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong) UITableView *table1;@property (nonatomic, strong) UITableView *table2;@end
兩個TableView的初始化以及代理方法就不展開了,可以看到Demo,主要介紹個方法,讓TableView是否能跟著整體
滾動與否
#pragma mark <MXPageProtocol>- (BOOL)segmentedPager:(MXSegmentedPager *)segmentedPager shouldScrollWithView:(UIView *)view { // 返回NO就是代表是獨立的 if (view == self.table2) { return NO; } // YES就是整體的 return YES;}
很好玩的東西竟然差點忘了
Demo地址:https://github.com/DeftMKJ/blur
OVER~~~~~~
iOS 網易新聞首頁進化版Demo(MXSegmentedPager),內建平行頭部展開