新闻类APP模板详解(iOS版)

1.概述

用过头条、网易等新闻客户端的用户是不是觉得页面很简洁、好用!那这类应用是如何实现的?有没有可以直接拿来使用的开源模板呢?
本文就提供了一个模板,是由作者整合了网上各类资源后整理出来的,希望对大家有所帮助!

android版新闻类app模板详见:
新闻类APP模板详解(android版)

环境:xcode 5.1.1,iOS SDK 7.1

先上图:
新闻类APP模板详解(iOS版)_第1张图片  新闻类APP模板详解(iOS版)_第2张图片

2.具体实现

要想实现该模板,需要完成以下几个重要组成部分。

(1)主页左右滑动的ViewPager

主页里显示各类新闻专题的页面是一个viewpager,响应用户左右滑动切换专题页。

该部分参考了 ICViewPager,是Code4App上的一个开源工程,效果图如下:

新闻类APP模板详解(iOS版)_第3张图片

回到本模板!
我们的主视图控制器(GTMainViewController)继承了ICViewPager的ViewPagerController,需要实现一些protocol方法。
其中,ViewPagerDataSource就非提不可了,先看代码:
#pragma mark - ViewPagerDataSource
// Tab数量
- (NSUInteger)numberOfTabsForViewPager:(ViewPagerController *)viewPager {
    return categoryList.count;
}

// “类别-0”、“类别-1”这些Tab内容添加处
- (UIView *)viewPager:(ViewPagerController *)viewPager viewForTabAtIndex:(NSUInteger)index {
    
    GTCategoryItem *item = [categoryList objectAtIndex:index];
    
    UILabel *label = [UILabel new];
    label.backgroundColor = [UIColor clearColor];
    label.font = [UIFont systemFontOfSize:13.0];
    if (item) {
        label.text = item.categoryName;
    }
    label.textAlignment = NSTextAlignmentCenter;
    label.textColor = [UIColor blackColor];
    [label sizeToFit];
    
    return label;
}

// 每个Tab对应的视图控制器(也就是新闻列表页面)
- (UIViewController *)viewPager:(ViewPagerController *)viewPager contentViewControllerForTabAtIndex:(NSUInteger)index {
    
    GTNewsViewController *vCtrl = [self.storyboard instantiateViewControllerWithIdentifier:@"NewsViewController"];
    [vCtrl setPViewCtrl:self];
    return vCtrl;
}
其中,categoryList里存放着多个新闻专题信息的对象,是一个列表。
第一个方法里,categoryList . count返回的就是新闻专题种类,也就是Tab数量。
第二个方法里,创建了每个Tab控件的主体,本例子创建了一个UILabel,内容填充未新闻专题名字( item. categoryName)。
第三个方法里,创建了每个Tab对应的视图控制器,本例子里每个Tab所属的viewcontroller是GTNewsViewController,是一个UITableViewController,显示具体某类新闻列表。

除此之外,还要实现ViewPagerDelegate协议方法,用于设定一些显示属性,代码如下:
#pragma mark - ViewPagerDelegate
- (CGFloat)viewPager:(ViewPagerController *)viewPager valueForOption:(ViewPagerOption)option withDefault:(CGFloat)value {
    CGFloat result = 0.0;
    switch (option) {
        case ViewPagerOptionStartFromSecondTab:
            result = 1.0;
            break;
        case ViewPagerOptionCenterCurrentTab:
            result = 0.0;
            break;
        case ViewPagerOptionTabLocation:
            result = 1.0;
            break;
        case ViewPagerOptionTabWidth:
            result = self.view.frame.size.width / 5;
            break;
        default:
            result = value;
            break;
    }
    
    return result;
}
- (UIColor *)viewPager:(ViewPagerController *)viewPager colorForComponent:(ViewPagerComponent)component withDefault:(UIColor *)color {
    
    switch (component) {
        case ViewPagerIndicator:
            return [[UIColor redColor] colorWithAlphaComponent:0.64];
            break;
        default:
            break;
    }
    
    return color;
}

本例子程序里ICViewPager模块源码详见3rd/ICViewPager文件夹。

(2)列表上下拖拽自动更新

当用户上拉或下拉新闻列表时,会自动获取更多新闻消息,所谓的自动刷新。
这部分主要参考了 MJRefresh,GitHub上的开源库(源码),效果图如下:

新闻类APP模板详解(iOS版)_第4张图片



回到本模板!
显示新闻列表的视图控制器是GTNewsViewController,上面提到过。该类集成了MJRefresh的刷新功能。
首先,引入头文件。
#import "MJRefresh.h"
其次,设置刷新控件。
/**
 *  集成刷新控件
 */
- (void)setupRefresh
{
    // 1.下拉刷新(进入刷新状态就会调用self的headerRereshing)
    [self.tableView addHeaderWithTarget:self action:@selector(headerRereshing)];
    //    [self.tableView headerBeginRefreshing];
    
    // 2.上拉加载更多(进入刷新状态就会调用self的footerRereshing)
    [self.tableView addFooterWithTarget:self action:@selector(footerRereshing)];
}
最后,实现顶部刷新( headerRereshing)和底部刷新( footerRereshing)方法。
- (void)headerRereshing
{
    for (int i = 0; i < 5; i++) {
        GTNewsItem *item = [[GTNewsItem alloc] init];
        item.newsID = [NSString stringWithFormat:@"%d", i];
        item.newsTitle = [NSString stringWithFormat:@"热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻 -- %d", i];
        item.newsFrom = @"腾讯新闻";
        item.newsPic = nil;
        item.timestamp = [[NSDate date] timeIntervalSince1970] * 1000;
        item.readCount = i * 1000;
        item.commentCount = i * 2000;
        
        [arrNews insertObject:item atIndex:0];
    }
    
    [self.tableView reloadData];
    [self.tableView headerEndRefreshing];
}

- (void)footerRereshing
{
    for (int i = 0; i < 5; i++) {
        GTNewsItem *item = [[GTNewsItem alloc] init];
        item.newsID = [NSString stringWithFormat:@"%d", i];
        item.newsTitle = [NSString stringWithFormat:@"热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新闻热点新 -- %d", i];
        item.newsFrom = @"搜狐新闻";
        item.newsPic = nil;
        item.timestamp = [[NSDate date] timeIntervalSince1970] * 1000;
        item.readCount = i * 10;
        item.commentCount = i;
        
        [arrNews addObject:item];
    }
    
    [self.tableView reloadData];
    [self.tableView footerEndRefreshing];
}

其中,arrNews存放着多条新闻消息,两个方法新增消息后刷新一下tableView。

本例子程序里MJRefresh模块源码详见3rd/MJRefresh文件夹。

(3)新闻列表顶部广告栏

本模板新闻列表顶部还插入广告栏,可以滚动显示。其实,就是将列表第一条记录设置成广告控件啦。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger row = indexPath.row;
    UITableViewCell *cell;
    if (row == 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
    }
    
    
    float contentWid = self.view.frame.size.width - 3 * gtNewsPadding;
    
    //定义新的cell
    if(cell == nil)
    {
        if (row == 0) {
            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1];
            
            ASScroll *asScroll = [[ASScroll alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.width * 0.3)];
            asScroll.tag = news_content_ads;
            [cell.contentView addSubview:asScroll];
            
        } else {
            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier2];
            
            // 新闻图片
            CGRect picRect = CGRectMake(gtNewsPadding, gtNewsPadding, contentWid / 3, contentWid / 4);
            UIImageView *ivPic = [[UIImageView alloc] initWithFrame:picRect];
            ivPic.tag = news_content_pic;
            [cell.contentView addSubview:ivPic];
            
            // 新闻标题
            UILabel *lbTitle = [[UILabel alloc] init];
            lbTitle.font = [UIFont boldSystemFontOfSize:gtFontSizeBig];
            lbTitle.tag = news_content_title;
            lbTitle.textColor = [UIColor brownColor];
            [cell.contentView addSubview:lbTitle];
            
            // 新闻来源
            UILabel *lbFrom = [[UILabel alloc] init];
            lbFrom.font = [UIFont boldSystemFontOfSize:gtFontSizeSmall];
            lbFrom.tag = news_content_from;
            lbFrom.textColor = [UIColor grayColor];
            [cell.contentView addSubview:lbFrom];
            
            // 新闻阅读数量(图标)
            UIImageView *ivReadCount = [[UIImageView alloc] init];
            ivReadCount.tag = news_content_read_img;
            [cell.contentView addSubview:ivReadCount];
            
            // 新闻阅读数量
            UILabel *lbReadCount = [[UILabel alloc] init];
            lbReadCount.font = [UIFont boldSystemFontOfSize:gtFontSizeSmall];
            lbReadCount.tag = news_content_read_count;
            lbReadCount.textColor = [UIColor grayColor];
            [cell.contentView addSubview:lbReadCount];
            
            // 评论数量(图片)
            UIImageView *ivCommentCount = [[UIImageView alloc] init];
            ivCommentCount.tag = news_content_comment_img;
            [cell.contentView addSubview:ivCommentCount];
            
            // 评论数量
            UILabel *lbCommentCount = [[UILabel alloc] init];
            lbCommentCount.font = [UIFont boldSystemFontOfSize:gtFontSizeSmall];
            lbCommentCount.tag = news_content_comment_count;
            lbCommentCount.textColor = [UIColor grayColor];
            [cell.contentView addSubview:lbCommentCount];
        }
    }
    
    if (row == 0) {
        ASScroll *asScroll = (ASScroll *)[cell.contentView viewWithTag:news_content_ads];
        
        NSMutableArray * imagesArray = [[NSMutableArray alloc] init];
        int noOfImages = 3 ;
        for (int imageCount = 0; imageCount < noOfImages; imageCount++)
        {
            [imagesArray addObject:[UIImage imageNamed:[NSString stringWithFormat:@"ad_%d.jpg",imageCount+1]]];
        }
        [asScroll setArrOfImages:imagesArray];
    } else {
        GTNewsItem *obj = [arrNews objectAtIndex:(row - 1)];
        
        float fHei = gtNewsPadding;
        
        // 新闻图片
        UIImageView *ivPic = (UIImageView *)[cell.contentView viewWithTag:news_content_pic];
        ivPic.image = [UIImage imageNamed:@"placeholder_logo.png"];
        
        // 新闻标题
        UILabel *lbTitle = (UILabel *)[cell.contentView viewWithTag:news_content_title];
        CGSize ttlSize = {0, 0};
        ttlSize = [obj.newsTitle sizeWithFont:[UIFont systemFontOfSize:gtFontSizeBig]
                            constrainedToSize:CGSizeMake(2 * contentWid / 3, 5000)
                                lineBreakMode:NSLineBreakByWordWrapping];
        lbTitle.numberOfLines = 0;  //表示label可以多行显示
        lbTitle.lineBreakMode = NSLineBreakByWordWrapping;  //换行模式
        CGRect ttlRect = CGRectMake(contentWid / 3 + 2 * gtNewsPadding, fHei, 2 * contentWid / 3, ttlSize.height);
        [lbTitle setFrame:ttlRect];
        lbTitle.text = obj.newsTitle;
        
        fHei += ttlSize.height + gtNewsPadding;
        
        // 控件位置 从右算起
        float widPos = self.view.frame.size.width - gtNewsPadding - 104;
        
        // 新闻来源
        UILabel *lbFrom = (UILabel *)[cell.contentView viewWithTag:news_content_from];
        float fromWid = widPos - (contentWid / 3 + 2 * gtNewsPadding + 4);
        CGRect fromRect;
        if (fromWid > 0) {
            fromRect = CGRectMake(contentWid / 3 + 2 * gtNewsPadding, fHei, fromWid, 16);
        } else {
            fromRect = CGRectMake(contentWid / 3 + 2 * gtNewsPadding, fHei, 0, 16);
        }
        [lbFrom setFrame:fromRect];
        lbFrom.text = obj.newsFrom;
        lbFrom.textAlignment = NSTextAlignmentLeft;
        
        // 阅读数量
        UILabel *rcLabel = (UILabel *)[cell.contentView viewWithTag:news_content_read_count];
        CGRect rcLRect = CGRectMake(widPos, fHei, 30, 16);
        [rcLabel setFrame:rcLRect];
        rcLabel.text = [NSString stringWithFormat:@"%ld",obj.readCount];
        rcLabel.textAlignment = NSTextAlignmentRight;
        widPos += 30 + 4;
        
        // 阅读数量(图标)
        UIImageView *ivReadCount = (UIImageView *)[cell.contentView viewWithTag:news_content_read_img];
        CGRect rcRect = CGRectMake(widPos, fHei, 16, 16);
        [ivReadCount setFrame:rcRect];
        ivReadCount.image = [UIImage imageNamed:@"icon_read_count.png"];
        widPos += 16 + 4;
        
        // 评论数量
        UILabel *ccLabel = (UILabel *)[cell.contentView viewWithTag:news_content_comment_count];
        CGRect ccLRect = CGRectMake(widPos, fHei, 30, 16);
        ccLabel.text = [NSString stringWithFormat:@"%ld",obj.commentCount];
        ccLabel.textAlignment = NSTextAlignmentRight;
        [ccLabel setFrame:ccLRect];
        widPos += 30 + 4;
        
        // 评论数量(图标)
        UIImageView *ivCommentCount = (UIImageView *)[cell.contentView viewWithTag:news_content_comment_img];
        CGRect ccRect = CGRectMake(widPos, fHei, 16, 16);
        [ivCommentCount setFrame:ccRect];
        ivCommentCount.image = [UIImage imageNamed:@"icon_comment_count.png"];
    }
    
    return cell;
}

当row等于0(第一行)时,填充内容换成了ASScroll控件,它是一个横向滚动的视图(view)。由于广告栏和消息记录对应的UITableViewCell不一样,所有我们创建了两种cell。
static NSString *CellIdentifier1 = @"NewsAdsCell";
static NSString *CellIdentifier2 = @"NewsListCell";

更多ASScroll相关信息,请参见 ASScrollView,是Code4App上的一个开源工程,本人做了一些改动。
本例子程序里ASScroll模块源码详见3rd/ASScroll文件夹。

(4)左侧抽屉菜单

这一段参考了 NewsFourApp,是Code4App上的一个开源工程,效果图如下:

新闻类APP模板详解(iOS版)_第5张图片 新闻类APP模板详解(iOS版)_第6张图片

回到本模板!
首先,到Main.storyboard,将应用入口设为slide navigation controller,对应的视图控制器为SlideNavigationController。SlideNavigationController是抽屉菜单核心类,其中有包含菜单添加、显示、隐藏等内容,详情见源码。
新闻类APP模板详解(iOS版)_第7张图片


然后,创建一个菜单类视图控制器页面,对应的类为GTMenuViewController。该视图包含一个UIImageView(头像)、UILable(昵称)和UITableView(菜单项列表)。
新闻类APP模板详解(iOS版)_第8张图片
然后,到AppDelegate文件GTAppDelegate.m里添加菜单初始化代码。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main"
															 bundle: nil];
    
    // 左侧菜单视图控制器
    GTMenuViewController *leftMenu = [mainStoryboard instantiateViewControllerWithIdentifier:@"MenuViewController"];
    leftMenu.view.backgroundColor = [UIColor lightGrayColor];
    
    // 无右侧菜单
    [SlideNavigationController sharedInstance].righMenu = nil;
    // 设置左侧菜单
	[SlideNavigationController sharedInstance].leftMenu = leftMenu;
    
    return YES;
}

最后,设置主视图控制器GTMainViewController。
实现SlideNavigationControllerDelegate协议方法:
#pragma mark - SlideNavigationController Methods
// 可以显示左侧菜单
- (BOOL)slideNavigationControllerShouldDisplayLeftMenu
{
	return YES;
}
// 不显示右侧菜单
- (BOOL)slideNavigationControllerShouldDisplayRightMenu
{
	return NO;
}
设置菜单控制器GTMenuViewController对象的父类控制器,用于页面跳转。这个很重要!因为GTMenuViewController类本身没有 navigationController,所以不能跳转页面。可以在GTMainViewController的viewDidLoad方法里初始化。
- (void)viewDidLoad
{
    ...
    
    GTMenuViewController *lMenu = (GTMenuViewController *)[SlideNavigationController sharedInstance].leftMenu;
    [lMenu setPViewCtrl:self];
    
    ...
    
    [super viewDidLoad];
}

谢天谢地,总算讲完了!不知道大伙能不能理解,不理解的可以看源码,可能更直观一些!

3.源码


工程源码








你可能感兴趣的:(ios)