1.概述
用过头条、网易等新闻客户端的用户是不是觉得页面很简洁、好用!那这类应用是如何实现的?有没有可以直接拿来使用的开源模板呢?
本文就提供了一个模板,是由作者整合了网上各类资源后整理出来的,希望对大家有所帮助!
android版新闻类app模板详见:
新闻类APP模板详解(android版)
环境:xcode 5.1.1,iOS SDK 7.1
先上图:
2.具体实现
要想实现该模板,需要完成以下几个重要组成部分。
(1)主页左右滑动的ViewPager
主页里显示各类新闻专题的页面是一个viewpager,响应用户左右滑动切换专题页。
该部分参考了ICViewPager,是Code4App上的一个开源工程,效果图如下:
回到本模板!
我们的主视图控制器(GTMainViewController)继承了ICViewPager的ViewPagerController,需要实现一些protocol方法。
其中,ViewPagerDataSource就非提不可了,先看代码:
- #pragma mark - ViewPagerDataSource
-
- - (NSUInteger)numberOfTabsForViewPager:(ViewPagerController *)viewPager {
- return categoryList.count;
- }
-
-
- - (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;
- }
-
-
- - (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上的开源库(源码),效果图如下:
回到本模板!
显示新闻列表的视图控制器是GTNewsViewController,上面提到过。该类集成了MJRefresh的刷新功能。
首先,引入头文件。
-
-
-
- - (void)setupRefresh
- {
-
- [self.tableView addHeaderWithTarget:self action:@selector(headerRereshing)];
-
-
-
- [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 - 33 * gtNewsPadding;
-
-
- 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(22 * contentWid / 3, 5000)
- lineBreakMode:NSLineBreakByWordWrapping];
- lbTitle.numberOfLines = 0;
- lbTitle.lineBreakMode = NSLineBreakByWordWrapping;
- CGRect ttlRect = CGRectMake(contentWid / 3 + 22 * gtNewsPadding, fHei, 22 * 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 + 22 * gtNewsPadding + 4);
- CGRect fromRect;
- if (fromWid > 0) {
- fromRect = CGRectMake(contentWid / 3 + 22 * gtNewsPadding, fHei, fromWid, 16);
- } else {
- fromRect = CGRectMake(contentWid / 3 + 22 * 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上的一个开源工程,效果图如下:
回到本模板!
首先,到Main.storyboard,将应用入口设为slide navigation controller,对应的视图控制器为SlideNavigationController。SlideNavigationController是抽屉菜单核心类,其中有包含菜单添加、显示、隐藏等内容,详情见源码。
然后,创建一个菜单类视图控制器页面,对应的类为GTMenuViewController。该视图包含一个UIImageView(头像)、UILable(昵称)和UITableView(菜单项列表)。
然后,到AppDelegate文件GTAppDelegate.m里添加菜单初始化代码。
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main"
- bundle: nil 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.源码
工程源码