类似zakerApp里横向显示新闻功能的实现

实现效果图:



实现过程:

zaker是一个新闻杂志类app,因此新闻对内容的展现形式是多样化的。本人现在的一个项目某个模块就是仿照zaker写的,所以才会研究它如何去实现。值得一提的是,一直往右翻翻到最后一页并不会在右边显示一个加载的视图,而是在即将翻到最后一页的某一页的时候发起加载下一页的请求,在最开始的时候本来是用scrollView写的,利用scrollView的各种代理方法以及各种属性来判断当前显示第几页,判断在什么条件下发起下一页的请求(这里面的细节很多)。做完以后,总感觉有问题,因为太复杂了。后来还考虑到复用,顿时觉得绝望了。尴尬

后面灵机一动,不是还有tableView嘛,旋转一下就可以了,瞬间觉得茅塞顿开。下面是代码以及实现过程:


首先先解决各种版面(也就是当前页面的)的问题,一个版面里面有6条新闻,这六条新闻里面,有一个大的图片,一个大的文本框,还有四个普通大小的文本框。对应应该写三个文件BigImageItem、BigLabelItem、NormalItem,在这三个文件里面分别把self的大小固定、其上面的各个子视图位置根据父视图的值来设置。但是我把这三种类型的item写在同一文件里面:

#import <UIKit/UIKit.h>
typedef enum{
    kChannelItemTypeDefault,
    kChannelItemTypeBigImage,
    kChannelItemTypeBigLabel
}ChannelItemType;

@interface ChannelItem : UIButton//注意,这里是集成UIButton哦,大图片就是self的backgroundImage
{
    CGFloat _width;
    CGFloat _height;
}
@property(nonatomic,strong)UILabel *nameLabel;
@property(nonatomic,strong)UILabel *authorLabel;
@property(nonatomic,assign)ChannelItemType channelItemType;
@property(nonatomic,strong)UIView *view;
+ (id)channelItemWithType:(ChannelItemType)channelItemType;
- (void)setTitleLabelText:(NSString *)text;
@end
#import "ChannelItem.h"

@implementation ChannelItem

- (void)setHighlighted:(BOOL)highlighted
{   
}

+ (id)channelItemWithType:(ChannelItemType)channelItemType
{
    SFChannelItem *item = [self buttonWithType:UIButtonTypeCustom];
    if (item) {
        item.channelItemType = channelItemType;
    }
    return item;

}

- (void)setFrame:(CGRect)frame
{
    [super setFrame:frame];
    _width = frame.size.width;
    _height = frame.size.height;
    [self makeChannelItem];
}

-(void)makeChannelItem
{
    
    if (_view == nil) {
        //实例化<span style="font-family: Menlo;">_nameLabel、</span><span style="font-family: Menlo;">authorLabel以及_view,要注意,可能会有子子视图挡住self点击不到的情况。</span><pre name="code" class="objc" style="color: rgb(201, 27, 19);">//因为item上面有来源跟标题,来源固定在标题的下方,根据标题的大小来源跟标题的位置也会对应变化,所以将他们放在view,固定view的宽度,高度随文字数量变化,然后调整view的位置,保证其居中
} }- (void)setTitleLabelText:(NSString *)text{//因为对应不同类型item的位置不同,所以要先判断是那种类型item,然后对应得出上面view的位置 _nameLabel.text = text; if (self.channelItemType != kChannelItemTypeBigImage) { NSDictionary *attributeDic = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}; CGSize size = [text boundingRectWithSize:CGSizeMake(_width - 20, _height - 30) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading|NSStringDrawingTruncatesLastVisibleLine attributes:attributeDic context:nil].size; [_nameLabel setFrame:CGRectMake(0, 0, _width - 20, size.height)]; [_authorLabel setFrame:CGRectMake(0, size.height, _width - 20, 20)]; if (self.channelItemType == kChannelItemTypeDefault) { [_view setBounds:CGRectMake(0, 0, _width - 20, size.height + 20)]; } else //如果是大的框 { [_view setBounds:CGRectMake(0, 0, _width - 20, size.height + 20)]; } [_view setCenter:CGPointMake(_width / 2, _height / 2)]; } else {//大图item并没有显示来源,所以跟上面不一样。又因为上面图片颜色可能不一样,所以需要给titleLabel加阴影 [_nameLabel setFrame:CGRectMake(10, _height - 25, _width - 20, 25)]; _nameLabel.textAlignment = NSTextAlignmentLeft; _nameLabel.layer.shadowColor = [UIColor blackColor].CGColor; _nameLabel.layer.shadowOffset = CGSizeMake(2, 2); _nameLabel.layer.shadowOpacity = 0.3; _nameLabel.layer.shadowRadius = 0.3; _nameLabel.clipsToBounds = NO; [_nameLabel setFont:[UIFont boldSystemFontOfSize:16]]; [_nameLabel setTextColor:[UIColor whiteColor]]; [_authorLabel removeFromSuperview]; }}@end

 

确定版面上三种Item以后,开始真正实现版面了,因为本项目目前的版面只有一种,所以还并没有跟服务器商量代表版面形式的那个字段的数据格式类型,以及都有哪些版面类型。不过,为后续开发便利,本人还是要考虑提供可以设置不同版面的功能。

#import <UIKit/UIKit.h>

#import "ChannelItem.h"

@class ChannelView;

@protocol ChannelViewDelegate <NSObject>

- (void)channelView:(SFChannelView *)channelView btnClickAtIndex:(NSInteger)index;

@end

@interface ChannelView : UITableViewCell


@property(nonatomic,strong)UIImageView *bigImage;
@property(nonatomic,strong)NSArray *btnDataArray;
@property(nonatomic,weak)id <SFChannelViewDelegate>delegate;
@property(nonatomic,assign)ChannelItemType channelItemType;

- (id)initWithFrame:(CGRect)frame andDataArray:(NSArray *)dataArray;
- (void)reloadData:(NSArray *)dataArray;

@end

#import "ChannelView.h"
#import "NewsModel.h"
#import "UIButton+WebCache.h"


@interface ChannelView ()
{
    CGFloat _bigItemHeight;
    CGFloat _itemWidth;
    CGFloat _itemHeight;
    
    NSInteger _bigImageIndex;
    NSInteger _bigLabelIndex;
    
    NSMutableArray *_itemArray;
    NSMutableArray *_flagArray;
}
@end

@implementation ChannelView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
    }
    return self;
}


- (id)initWithFrame:(CGRect)frame andDataArray:(NSArray *)dataArray
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor whiteColor]];
        // 这里需要传入一个版式样式,根据其得出特殊item的下标,下面的位置是自己写的,如果想要调整可以改正下面的值
        _bigImageIndex = 0;
        _bigLabelIndex = 5;
        
        _itemArray = [[NSMutableArray alloc] initWithCapacity:dataArray.count];
        _flagArray = [[NSMutableArray alloc] initWithCapacity:dataArray.count];
        
        // 利用标记确定每张图片的位置
        [self makeFlages:(dataArray.count + 2)/ 2];
        
        // 计算普通图片跟特殊图片的宽高
        _itemWidth = (frame.size.width - 1) / 2;
        _bigItemHeight = frame.size.width * kImageScale;
        _itemHeight = (frame.size.height - _bigItemHeight) / (dataArray.count / 2);
        
        // 一个个添加item
        for (int i = 0; i < dataArray.count; i++) {
            [self makeItemAtIndex:i dataModel:dataArray[i]];
        }
    }
    return self;
}


- (void)makeItemAtIndex:(NSInteger)index dataModel:(NewsModel *)model
{
    if (index == _bigImageIndex) {
        _channelItemType = kChannelItemTypeBigImage;
    }
    else if(index == _bigLabelIndex)
    {
        _channelItemType = kChannelItemTypeBigLabel;
    }
    else
    {
        _channelItemType = kChannelItemTypeDefault;
    }

    SFChannelItem *item = [SFChannelItem channelItemWithType:_channelItemType];
    NSInteger flag = [_flagArray[index] intValue];
    CGFloat x;
    CGFloat y;
    CGFloat width = _itemWidth;
    CGFloat height = _itemHeight;
    // 确定每个item的位置,宽高全部为普通宽高
    if (index <= _bigImageIndex) { //图片的前面
        x = flag % 10 * _itemWidth;
        y = flag / 10 * _itemHeight;
    }
    else //图片的后面
    {
        x = flag % 10 * _itemWidth;
        y = (flag / 10 - 1) * _itemHeight + _bigItemHeight;
    }
    <p>//因为本人的美工做了一个很坑爹的UI,普通item之间的中间那根线的长度不一样,因为项目是企业项目,目前还没有上线,所以不能截图看效果。各位只要知道,如果是一样长的分隔线,会简单很多就行了。所以下面很多if-else语句,可能我要看懂都得花点时间。</p>    // 修改特殊item的宽高
    if (index == _bigImageIndex) {
        width *= 2;
        width++;
        height = _bigItemHeight;
    }
    else if (index == _bigLabelIndex)
    {
        width *= 2;
        width++;
    }
    else //普通的
    {
        // 给普通的加竖线,思路跟代码如下,如果要扩展,以后再说。反正现在是实现了
        if (flag % 2) {//如果是右边的item,观察得里面的代码只会执行两次
            // 在正中间的位置加上一根直线
            static int i = 0;
            int number = (int)flag / 10;
            static int value = 0;
            if (i == 0) {//第一次
                value = number;
                //第一次的时候
                UIView *line = [[UIView alloc] initWithFrame:CGRectMake(x, y + 20, 1, height - 20)];
                [line setBackgroundColor:tintColor];
                [self.contentView addSubview:line];

            }
            else //第二次
            {
                if(abs(value - number) == 1)
                {
                    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(x, y, 1, height)];
                    [line setBackgroundColor:tintColor];
                    [self.contentView addSubview:line];
                }
                else
                {
                    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(x, y + 20, 1, height - 20)];
                    [line setBackgroundColor:tintColor];
                    [self.contentView addSubview:line];
                }
                
            }
            i++;
            x++;
        }
        // 加横线的时候,先判断下一个是不是大图,如果是大图就不给加横线,如果不是大图,就加横线
        if(index != _bigImageIndex - 1)
        {
            CGFloat x = 0;
            if (flag % 2 == 0) {//左边
                x = 5;
            }
            UIView *henLine = [[UIView alloc] initWithFrame:CGRectMake(x, height - 1, width - 5, 1)];
            [henLine setBackgroundColor:tintColor];
            [item addSubview:henLine];
        }
        
    }
    
    [item setFrame:CGRectMake(x, y, width, height)];
    item.tag = index;
    
    // 设置根据model设置item上面显示的内容
    [item addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
//    item.nameLabel.text = model.title;
    [item setTitleLabelText:model.title];
    item.authorLabel.text = model.author;
//    item.timeLabel.text = @"两小时";
    [self.contentView addSubview:item];
    [_itemArray addObject:item];
    
    
}

// 生成标记
- (void)makeFlages:(NSInteger)number
{//标记数组的形式如下:[0,10,11,20,21,30],第一位代表第几行,第二位代表第几列。也就是位置在左边还是在右边,然后根据标记就能知道对应item的位置以及frame了
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:number * 2];
    for (NSInteger i = 0; i < number; i++) {
        [array addObject:[NSString stringWithFormat:@"%ld",(long)i * 10]];
        [array addObject:[NSString stringWithFormat:@"%ld",(long)i * 10 + 1]];
    }
    for (NSInteger i = 0; i < array.count; i++) {
        [_flagArray addObject:array[i]];
        if (_flagArray.count == _bigImageIndex + 1 || _flagArray.count == _bigLabelIndex + 1) {
            i++;
        }
    }
}

- (void)reloadData:(NSArray *)dataArray
{
    for (int i = 0; i < _itemArray.count; i++) {
        SFChannelItem *item = _itemArray[i];
        NewsModel *model = dataArray[i];
        item.nameLabel.text = model.title;
        item.authorLabel.text = model.author;
        [item sd_setBackgroundImageWithURL:[NSURL URLWithString:model.imagePath] forState:UIControlStateNormal placeholderImage:[UIImage imageNamed:@""]];
    }
}

- (void)btnClick:(UIButton *)btn
{//调用代理方法,不多说了
    if ([self.delegate respondsToSelector:@selector(channelView:btnClickAtIndex:)]) {
        [self.delegate channelView:self btnClickAtIndex:(long)btn.tag];
    }
}

写到这里的时候,我已经不想再写了。。。但是,我还是得做一个善始善终的人。。。

注意上面的ChannelView是继承UITableViewCell的

下面开始说滑动的实现了

- (void)startRequest
{
    NSString *urlStr = [NSString stringWithFormat:@"%@/page/%@/%ld",kServerIp,self.channelId,_currentPage];
    if (_currentPage >= _totalPage) {//因为这里有一个bug,所以必需加上判断
        return;
    }
    [XSLHTTPManager requestWithUrl:urlStr FinishBlock:^(NSData *data) {
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSArray *array = dic[@"data"];
        // 求出总页面数
        [self turnArray:array toArray:_newsArray];
        _sectionsInPage = [dic[@"pageSize"] integerValue];
        _totalPage = [dic[@"totalPage"] integerValue];
        _isRequest = NO;
        [_tableView reloadData];
        [self makeButtomView];
        } FailedBlock:^{
    }];
}

- (void)turnArray:(NSArray *)array toArray:(NSMutableArray *)muArray
{
    for (NSDictionary *kDic in array) {
        NSArray *newsArray =kDic[@"newsItemsVO"];
        NSMutableArray *tmpArr = [[NSMutableArray alloc] initWithCapacity:0];
        @autoreleasepool {
            for (NSDictionary *newsDic in newsArray) {
                NewsModel *model = [[NewsModel alloc] init];
                [model setValuesForKeysWithDictionary:newsDic];
                model.newsId = newsDic[@"contentId"];
                if (model.authorName == nil) {
                    model.authorName = self.title;
                }
                [tmpArr addObject:model];
            }
        }
        [muArray addObject:tmpArr];
    }
}<p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">- (<span style="color: #703daa">CGFloat</span>)tableView:(<span style="color: #6122ae">UITableView</span> *)tableView heightForRowAtIndexPath:(<span style="color: #6122ae">NSIndexPath</span> *)indexPath</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">{</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; color: rgb(120, 73, 42);"><span style="color: #000000">    </span><span style="color: #c32275">return</span><span style="color: #000000"> </span>kScreenWidth<span style="color: #000000">;</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">}</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; min-height: 14px;">
</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">- (<span style="color: #703daa">NSInteger</span>)tableView:(<span style="color: #6122ae">UITableView</span> *)tableView numberOfRowsInSection:(<span style="color: #703daa">NSInteger</span>)section</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">{</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; color: rgb(83, 154, 164);"><span style="color: #000000">    </span><span style="color: #c32275">return</span><span style="color: #000000"> </span>_newsArray<span style="color: #000000">.</span><span style="color: #703daa">count</span><span style="color: #000000">;</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">}</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; min-height: 14px;">
</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">- (<span style="color: #6122ae">UITableViewCell</span> *)tableView:(<span style="color: #6122ae">UITableView</span> *)tableView cellForRowAtIndexPath:(<span style="color: #6122ae">NSIndexPath</span> *)indexPath</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">{</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; color: rgb(61, 29, 129);"><span style="color: #000000">    </span><span style="color: #3c828c">SFChannelView</span><span style="color: #000000"> *cell = [tableView </span>dequeueReusableCellWithIdentifier<span style="color: #000000">:</span><span style="color: #c91b13">@"cell"</span><span style="color: #000000">];</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    [<span style="color: #c32275">self</span> <span style="color: #294c50">loadDataWith</span>:<span style="color: #539aa4">_newsArray</span>[indexPath.<span style="color: #703daa">row</span>]];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    <span style="color: #c32275">if</span> (cell == <span style="color: #c32275">nil</span>) {</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">        cell = [[<span style="color: #3c828c">SFChannelView</span> <span style="color: #3d1d81">alloc</span>] <span style="color: #294c50">initWithFrame</span>:<span style="color: #3d1d81">CGRectMake</span>(<span style="color: #0435ff">0</span>, <span style="color: #0435ff">0</span>, <span style="color: #78492a">kScreenWidth</span>, <span style="color: #539aa4">_tableView</span>.<span style="color: #703daa">frame</span>.<span style="color: #703daa">size</span>.<span style="color: #703daa">height</span>) <span style="color: #294c50">andDataArray</span>:<span style="color: #539aa4">_dataArray</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; color: rgb(61, 29, 129);"><span style="color: #000000">        cell.</span><span style="color: #703daa">transform</span><span style="color: #000000"> = </span>CGAffineTransformMakeRotation<span style="color: #000000">(</span><span style="color: #78492a">M_PI_2</span><span style="color: #000000">);</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">        cell.<span style="color: #539aa4">delegate</span> = <span style="color: #c32275">self</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    }</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    [cell <span style="color: #294c50">reloadData</span>:<span style="color: #539aa4">_dataArray</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    <span style="color: #539aa4">_currentSection</span> = indexPath.<span style="color: #703daa">row</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: 'Heiti SC Light'; color: rgb(29, 148, 33);"><span style="font-family: Menlo; color: rgb(0, 0, 0);">    </span><span style="font-family: Menlo;">// </span>在什么时候开始发起网络请求<span style="font-family: Menlo;">?</span>在第四页的时候,在总页数没有记载完的时候,在并没有正在加载网络请求的时候</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: 'Heiti SC Light'; color: rgb(29, 148, 33);"><span style="font-family: Menlo; color: rgb(0, 0, 0);">    </span><span style="font-family: Menlo;">// </span>这里有一个很奇怪的问题,竟然会同时调用这个方法两次,即使加了线程锁也没办法</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    <span style="color: #6122ae">NSLock</span> *lock = [[<span style="color: #6122ae">NSLock</span> <span style="color: #3d1d81">alloc</span>] <span style="color: #3d1d81">init</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    [lock <span style="color: #3d1d81">lock</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    <span style="color: #c32275">if</span> (<span style="color: #539aa4">_totalPage</span> > <span style="color: #539aa4">_currentPage</span> && indexPath.<span style="color: #703daa">row</span> % <span style="color: #0435ff">6</span> == <span style="color: #0435ff">3</span> && !<span style="color: #539aa4">_isRequest</span>) {</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">        <span style="color: #539aa4">_isRequest</span> = <span style="color: #c32275">YES</span>;</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo; color: rgb(83, 154, 164);"><span style="color: #000000">        </span>_currentPage<span style="color: #000000">++;</span></p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">        [<span style="color: #c32275">self</span> <span style="color: #294c50">startRequest</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    }</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    [lock <span style="color: #3d1d81">unlock</span>];</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">    <span style="color: #c32275">return</span> cell;</p><p style="margin-top: 0px; margin-bottom: 0px; font-family: Menlo;">}</p>



基本上就是这些了,终于写完了。各位能看完也是神了。。。。如果有不同的意见或者问题可以说出来一起讨论,本人虚心求教。大笑


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值