iOS 网易新闻首页进化版Demo(MXSegmentedPager),自带平行头部拉伸
Posted Deft_MKJing宓珂璟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS 网易新闻首页进化版Demo(MXSegmentedPager),自带平行头部拉伸相关的知识,希望对你有一定的参考价值。
网易新闻首页类似的界面简直太常见了,需求不同自然做出来的效果不同了,之前用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框架控制器,头部和选择栏
- (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
以上是关于iOS 网易新闻首页进化版Demo(MXSegmentedPager),自带平行头部拉伸的主要内容,如果未能解决你的问题,请参考以下文章
iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)
ActionBar+DrawerLayout实现网易新闻客户端首页