往夜 -- 原来世界如此性感
2017-02-28 18:31
323 查看
往夜 -- 原来世界如此性感
项目简介
项目为纯代码编写, 项目周期为14天。APP分为三个大模块
往夜模块
精选模块
专题模块
项目展示分析
整体结构用到的三方轮子
项目时间轴
2017年2月7日
网络工具类编写
#import <Foundation/Foundation.h> @interface BaseNetManager : NSObject //GET + (id)GET:(NSString *)path param:(NSDictionary *)param completionHandler:(void(^)(id obj, NSError *error))completionHandler; //POST + (id)POST:(NSString *)path param:(NSDictionary *)param completionHandler:(void(^)(id obj, NSError *error))completionHandler; @end
//GET @implementation BaseNetManager + (id)GET:(NSString *)path param:(NSDictionary *)param completionHandler:(void (^)(id, NSError *))completionHandler { AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.requestSerializer.timeoutInterval = 10; manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html", @"application/json", @"text/json", @"text/javascript", @"text/plain", nil]; return [manager GET:path parameters:param progress:^(NSProgress * _Nonnull downloadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@", task.currentRequest.URL.absoluteString); !completionHandler ?: completionHandler(responseObject, nil); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@", error); }]; } //POST + (id)POST:(NSString *)path param:(NSDictionary *)param completionHandler:(void (^)(id, NSError *))completionHandler { AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.requestSerializer.timeoutInterval = 15; manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html", @"application/json", @"text/json", @"text/javascript", @"text/plain", nil]; return [manager POST:path parameters:param progress:^(NSProgress * _Nonnull uploadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"%@", task.currentRequest.URL.absoluteString); !completionHandler ?: completionHandler(responseObject, nil); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@", error); !completionHandler ?: completionHandler(nil, error); }]; } @end
2017年2月8日
#import "YGTabBarController.h" #import "YGHomeController.h" #import "YGEssenceController.h" #import "YGListController.h" #import "YGListFlowLayout.h" #import "YGPageController.h" @interface YGTabBarController () @end @implementation YGTabBarController - (void)viewDidLoad { [super viewDidLoad]; [self allPropertySetup]; [self setupAllControllers]; } #pragma mark - 全局属性 - (void)allPropertySetup { [UITabBar appearance].tintColor = YGRGBColor(67, 67, 67); [UINavigationBar appearance].tintColor = YGRGBColor(67, 67, 67); [[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: YGRGBColor(67, 67, 67)} forState:UIControlStateSelected]; [UIImageView appearance].contentMode = UIViewContentModeScaleAspectFill; [UIImageView appearance].clipsToBounds = YES; [UICollectionView appearance].backgroundColor = YGBgColor; [UIImageView appearance].contentMode = UIViewContentModeScaleAspectFill; [UIImageView appearance].clipsToBounds = YES; } #pragma mark - 创建所有tabBar子控制器 - (void)setupAllControllers { YGPageController *pageVC = [[YGPageController alloc] init]; pageVC.tabBarItem.image = @"nav_ic_home_default".yg_image; pageVC.tabBarItem.selectedImage = @"nav_ic_home_selected".yg_image; pageVC.title = @"往夜"; UINavigationController *homeNavi = [[UINavigationController alloc] initWithRootViewController:pageVC]; YGEssenceController *essenceVC = [[YGEssenceController alloc] initWithStyle:UITableViewStylePlain]; essenceVC.title = @"精选"; essenceVC.tabBarItem.image = @"tab_btn_list_default".yg_image; essenceVC.tabBarItem.selectedImage = @"tab_btn_list_select".yg_image; UINavigationController *essenceNavi = [[UINavigationController alloc] initWithRootViewController:essenceVC]; YGListController *listVC = [[YGListController alloc] initWithCollectionViewLayout:[[YGListFlowLayout alloc] init]]; listVC.title = @"专题"; listVC.tabBarItem.image = @"nav_ic_columns_default".yg_image; listVC.tabBarItem.selectedImage = @"nav_ic_columns_selected".yg_image; UINavigationController *listNavi = [[UINavigationController alloc] initWithRootViewController:listVC]; self.viewControllers = @[homeNavi, essenceNavi, listNavi]; } #pragma mark - 关闭设备自动旋转, 然后手动监测设备旋转方向来旋转avplayerView -(BOOL)shouldAutorotate{ return NO; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
2017年2月9日 - 2017年2月13日
往夜资讯, 我从稍微简单的页面入手 -- 精华模块
整体为TableViewController, 里面分为三种Cell
整体用masonry进行Layout布局
界面整体比较简单, 其中Cell自适应高度, 我使用的UITableView-FDTemplateLayoutCell进行高性能自适应高度, 前百度forkingdog团队, 现在的滴滴打车的iOS大神Sunnyxx发布的高性能Cell自动高度计算框架.
//高性能计算行高 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { YGEssenceResponseFeedsItem *feedsItem = self.essenceArr[indexPath.section]; if (feedsItem.type == 1) { return [tableView fd_heightForCellWithIdentifier:@"YGEssenceCommomCell" configuration:^(YGEssenceCommomCell *cell) { cell.iconIV.imageURL = feedsItem.image.yg_URL; }]; } if (feedsItem.type == 0) { return [tableView fd_heightForCellWithIdentifier:@"YGEssenceImageCell" configuration:^(YGEssenceImageCell *cell) { [cell.iconIV setImageWithURL:feedsItem.image.yg_URL options:YYWebImageOptionIgnoreAnimatedImage]; }]; } return [tableView fd_heightForCellWithIdentifier:@"YGEssenceBigCell" configuration:^(YGEssenceBigCell *cell) { cell.titleLB.text = feedsItem.post.title; cell.detailLB.text = feedsItem.post.des; [cell.iconIV setImageWithURL:feedsItem.image.yg_URL options:YYWebImageOptionIgnoreAnimatedImage]; }]; }
精华模块主要是资讯信息, 里面用到的是UIWebView来显示, 里面有一个退出按钮的小动画, 让我稍微琢磨了一小下。
#pragma mark - 创建悬浮按钮 - (void)creatSuspendButton { self.suspendBtn = [UIButton buttonWithType:UIButtonTypeCustom]; [self.suspendBtn setBackgroundImage:[UIImage imageNamed:@"homeBackButton"] forState:UIControlStateNormal]; self.suspendBtn.frame = CGRectMake(0, 0, 54, 54); [self.suspendBtn addTarget:self action:@selector(clickSuspendButton) forControlEvents:UIControlEventTouchUpInside]; //创建悬浮按钮的window self.buttonWin = [[UIView alloc] initWithFrame:CGRectMake(25, YGScreenH - 60, 54, 54)]; self.buttonWin.backgroundColor = [UIColor clearColor]; //将buttonWin显示出来 [self.view addSubview:self.buttonWin]; [self.buttonWin addSubview:self.suspendBtn]; } //点击悬浮按钮 - (void)clickSuspendButton { self.buttonWin.hidden = YES; 4000 self.buttonWin = nil; [self.navigationController popViewControllerAnimated:YES]; } #pragma mark - viewWillAppear - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.navigationController.navigationBarHidden = YES; } #pragma mark - viewViewDisappear - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.navigationController.navigationBarHidden = NO; } //按钮隐藏 #pragma mark - <UIScrollViewDelegate> - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.contentOffset.y > _offsetY + 1) { [self suspensionWithAlpha:0]; } else if (scrollView.contentOffset.y < _offsetY) { [self suspensionWithAlpha:1]; } } //停止滚动式调用 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { _offsetY = scrollView.contentOffset.y; } //设置悬浮按钮的透明度 - (void)suspensionWithAlpha:(CGFloat)alpha { [UIView animateWithDuration:0.3 animations:^{ [self.buttonWin setAlpha:alpha]; }]; }
具体示例
2017年2月14日 - 2017年2月15日
栏目模块结构
专题模块分为三次跳转
第一个界面
UICollectionViewController
第二个界面
UITableViewController
第三个界面播放界面
UIViewController
专题模块 和 列表详细整体masonry布局
列表详细播放中的播放我采用的是
HcdCachePlayer
HcdCachePlayer对于整体播放, 缓存有着良好的集成
#import "YGListDetailMovieController.h" @interface YGListDetailMovieController () { //播放器 HcdCacheVideoPlayer *_play; } /** 背景 */ @property(nonatomic, strong) UIImageView *bgView; /** 头像 */ @property(nonatomic, strong) UIImageView *headView; /** 详细 */ @property(nonatomic, strong) UILabel *detailLb; /** 影片简介 */ @property(nonatomic, strong) UILabel *constLabel; /** 播放按钮 */ @property(nonatomic, strong) UIButton *playBtn; // 计算属性, 屏幕大小 @property (nonatomic, assign) CGSize screenSize; @end @implementation YGListDetailMovieController - (instancetype)initWithBgImageView:(NSString *)bgImageView titleView:(NSString *)titleView detailLabel:(NSString *)titleLabel url:(NSString *)url; { self = [super init]; if (self) { self.titleView = titleView; self.bgImageView = bgImageView; self.detailLabel = titleLabel; self.url = url; self.hidesBottomBarWhenPushed = YES; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [self configUI]; } //配置播放界面 - (void)configUI { self.bgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, YGScreenW, YGScreenH)]; self.bgView.userInteractionEnabled = YES; [self.bgView setImageURL:self.bgImageView.yg_URL]; [self.view addSubview:self.bgView]; self.headView = [[UIImageView alloc] init]; [self.headView setImageURL:self.titleView.yg_URL]; self.headView.userInteractionEnabled = YES; [self.bgView addSubview:self.headView]; [self.headView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.offset(0); make.top.offset(64); CGFloat scale = 27 / 32.0; make.height.mas_equalTo(self.headView.mas_width).multipliedBy(scale); }]; self.constLabel = [[UILabel alloc] init]; self.constLabel.font = [UIFont boldSystemFontOfSize:18]; self.constLabel.textColor = [UIColor whiteColor]; self.constLabel.text = @"影片简介:"; [self.bgView addSubview:self.constLabel]; [self.constLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.left.offset(10); make.top.mas_equalTo(self.headView.mas_bottom).offset(10); }]; self.detailLb = [[UILabel alloc] init]; self.detailLb.text = self.detailLabel; self.detailLb.textColor = [UIColor whiteColor]; self.detailLb.numberOfLines = 0; [self.bgView addSubview:self.detailLb]; [self.detailLb mas_makeConstraints:^(MASConstraintMaker *make) { make.left.offset(10); make.right.offset(-10); make.top.mas_equalTo(self.constLabel.mas_bottom).offset(10); }]; self.playBtn = [UIButton buttonWithType:UIButtonTypeSystem]; [self.playBtn setImage:[UIImage imageNamed:@"play_button"] forState:UIControlStateNormal]; [self.headView addSubview:self.playBtn]; [self.playBtn mas_makeConstraints:^(MASConstraintMaker *make) { make.center.offset(0); make.size.mas_equalTo(60); }]; [self.playBtn addTarget:self action:@selector(playMovie) forControlEvents:UIControlEventTouchUpInside]; } //播放视频 - (void)playMovie { self.headView.hidden = YES; // self.constLabel.hidden = YES; // self.detailLb.hidden = YES; //点击后创建播放界面 _play = [[HcdCacheVideoPlayer alloc] init]; UIView *videoView = [[UIView alloc] initWithFrame:CGRectMake(0, 64, YGScreenW, YGScreenW * 27 / 32.0)]; [self.view addSubview:videoView]; //播放 [_play playWithUrl:self.url.yg_URL showView:videoView andSuperView:self.view withCache:YES]; NSLog(@"%@", NSHomeDirectory()); NSLog(@"%f", [HcdCacheVideoPlayer allVideoCacheSize]); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - 生命周期方法 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [_play stop]; #warning WAIT TODO:在这里先清除全部缓存 [HcdCacheVideoPlayer clearAllVideoCache]; } - (CGSize)screenSize { return [UIScreen mainScreen].bounds.size; } @end
视频展示
2017年2月16日
开始往夜模块编写插曲
将Model层, View层全部写完, Controller层全部写完, 就差播放视频, 发现视频加密, 显示的全部垃圾广告。一晚白忙。此时凌晨3:35。
2017年2月17日 - 2月19日
往夜 “往” 模块分为两种Cell
一种Cell为资讯展示
一种Cell为视频展示
往夜 “夜” 模块
”夜“模块
第一个界面为TableviewController
点入”更多专题“为UICollectionViewController
点入专题详细列表, 表头为拉伸图片, 下面是tableview
更多专题中有显示图片的专题
我用的是
MWPhotoBrowser这个框架
2017年2月20日
收尾
整体分析
项目主要以展示资讯,新闻,影视短片为主。项目架构简约,整体色调灰黑色。
整个项目没有xib 或者 sb参与
项目中细节梳理
头部无限轮播使用
iCarousel三方搭建。
本来有考虑使用自己写的一个无限轮播 框架,但是我没有别的封装,仅仅只是实现了高性能轮播。所以我是用了
iCarousel三方搭建。
#pragma mark - <ic Delegate> - (NSInteger)numberOfItemsInCarousel:(iCarousel *)carousel { return self.loopArr.count; } - (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view { if (!view) { view = [[UIView alloc] initWithFrame:carousel.bounds]; UIImageView *iconIV = [[UIImageView alloc] init]; [view addSubview:iconIV]; [iconIV mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.right.offset(0); CGFloat scale = 38 / 64.0; make.height.mas_equalTo(iconIV.mas_width).multipliedBy(scale); }]; iconIV.tag = 100; } YYAnimatedImageView *iconIV = [view viewWithTag:100]; [iconIV setImageWithURL:[NSURL URLWithString:self.loopArr[index]] options:YYWebImageOptionIgnoreAnimatedImage]; return view; } //只有变化时候才会来到这个方法 - (void)carouselCurrentItemIndexDidChange:(iCarousel *)carousel { self.titleLb.text = self.bannersArr[carousel.currentItemIndex].post.title; self.pc.currentPage = carousel.currentItemIndex; } - (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index { YGEssenceWebController *webVC = [[YGEssenceWebController alloc] initWithAppView:self.bannersArr[index].post.appview]; [self.navigationController pushViewController:webVC animated:YES]; } - (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value { if (option == iCarouselOptionWrap) { value = YES; } return value; }
“往夜”模块头部横向滚动
使用的是一款
WMPageController的三方控件
@interface YGPageController () @end @implementation YGPageController //初始化方法 - (instancetype)init { if (self = [super init]) { self.menuBGColor = YGRGBColor(249, 249, 249); self.menuViewStyle = WMMenuViewStyleLine; self.menuViewLayoutMode = WMMenuViewLayoutModeCenter; self.titleSizeNormal = 20; self.titleSizeSelected = self.titleSizeNormal; self.titleColorSelected = self.titleColorNormal; self.automaticallyCalculatesItemWidths = YES; //根据题目的内容自动算宽度 self.itemMargin = 30; //题目的间距 self.menuHeight = 44; self.showOnNavigationBar = YES; } return self; } - (NSArray<NSString *> *)titles { return @[@"往", @"夜"]; } - (NSInteger)numbersOfChildControllersInPageController:(WMPageController *)pageController { return self.titles.count; } - (UIViewController *)pageController:(WMPageController *)pageController viewControllerAtIndex:(NSInteger)index { if (index == 0) { YGHomeController *homeVC = [[YGHomeController alloc] init]; return homeVC; } YGCategoryController *cateVC = [[YGCategoryController alloc] init]; return cateVC; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. }
YG -- 阳光
业余时间的作品,每天晚上11点开始码代码,平均凌晨4点左右结束,早上9点还要忙自己的事情。如果觉得写得还不错请给予一颗小⭐️⭐️
GitHub地址
博客园地址
往夜 项目地址
相关文章推荐