往夜 -- 原来世界如此性感

来源:互联网 发布:视频剪辑软件 知乎 编辑:程序博客网 时间:2024/04/28 18:29

往夜 -- 原来世界如此性感

头像

往夜预览

项目简介

  • 项目为纯代码编写, 项目周期为14天。
  • APP分为三个大模块
    • 往夜模块
    • 精选模块
    • 专题模块

项目展示分析

  • 整体结构
    Snip20170221_1

  • 用到的三方轮子
    Snip20170221_2

项目时间轴

2017年2月7日

Snip20170221_3

  • 网络工具类编写
#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日

Snip20170221_4

#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日

Snip20170221_9

  • 往夜资讯, 我从稍微简单的页面入手 -- 精华模块
    Snip20170221_6

  • 整体为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;    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];    }];}
  • 具体示例
    往夜资讯 webVie

2017年2月14日 - 2017年2月15日

Snip20170221_10

  • 栏目模块结构
    Snip20170221_8

  • 专题模块分为三次跳转
    • 第一个界面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日

Snip20170221_11

  • 往夜 “往” 模块分为两种Cell
    Snip20170221_13

    • 一种Cell为资讯展示
    • 一种Cell为视频展示

Snip20170221_12

  • 往夜 “夜” 模块

Snip20170221_14

  • ”夜“模块
    • 第一个界面为TableviewController
  • 点入”更多专题“为UICollectionViewController
  • 点入专题详细列表, 表头为拉伸图片, 下面是tableview

往夜资讯 更过栏目

  • 更多专题中有显示图片的专题
    • 我用的是MWPhotoBrowser 这个框架
      往夜资讯 栏目图片

2017年2月20日

Snip20170221_16

  • 收尾

整体分析

  • 项目主要以展示资讯,新闻,影视短片为主。
  • 项目架构简约,整体色调灰黑色。
  • 整个项目没有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地址
博客园地址
往夜 项目地址


0 0
原创粉丝点击