iOS中如何优雅地处理票圈中的九图

在社交软件发达的今天,朋友圈的出现让每个人都能及时分享自己的生活,分享每时每刻成为了票圈最吸引人的地方。当我们要分享我们的生活时,就需要发布照片了。

但是,当照片的数量是5张,7张或者8张的时候,这就会显得非常尴尬了(这也是为什么很少有人发这个数量的票圈)。

作为强迫症,我相信99%的人都不会选择发这么尴尬的数量,要么宁愿少发点,要么就凑多几张。

现在,强迫症的福利来了!你将会看到这样的布局!这样的布局是不是即使遇上了五张或者七张这种尴尬的数量,也不会尴尬呢,嘻嘻~

下面来看看这个功能在iOS端是怎么实现的吧~

一.设计思路

单个票圈的布局中,一个Controller包括了很多个Cell,比如用户昵称Cell、图片Cell、评论点赞Cell等等,今天最主要设计的是图片的InfoCell。

二.各层设计

1. InfoCell

先来讲讲InfoCell是怎么设计的,因为这一层是核心,决定了整个图片的大小、位置等等。

首先,InfoCell采用UICollectionView来设计,这是因为UICollectionView更易于调整整个section的大小、布局等等。

在设计思路来说,InfoCell最重要的是需要根据自身的大小去决定每一个图片的大小。

比如说五图里面,前两个和后三个大小就很不一样,这时候就需要将他们分为两个part来讨论,5=2+3,7=3+4,而其他情况则可以直接按照原来的大小来设置:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    if (self.colorArray.count == 5 || self.colorArray.count == 7)
        return 2;
    else
        return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    if (self.colorArray.count == 5) {
        if (section == 0)
            return 2;
        else
            return 3;
    }
    else if (self.colorArray.count == 7) {
        if (section == 0)
            return 3;
        else
            return 4;
    }
    else
        return self.colorArray.count;
}

设置完Cell的数量之后,记得按情况获得每一个Cell的内容,还是要分情况讨论:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    HobenNinePhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: HobenNinePhotoID forIndexPath: indexPath];
    
    NSInteger currentItem = 0;
    if (self.colorArray.count == 5)
        currentItem = 2 * indexPath.section + indexPath.item;
    else if (self.colorArray.count == 7)
        currentItem = 3 * indexPath.section + indexPath.item;
    else
        currentItem = indexPath.item;
    
    [cell setColor: [self.colorArray objectAtIndex: currentItem]];
    
    return cell;
}

好的,最关键的地方来了,如何获得Cell的高宽的问题,这里我采用的是根据自身屏幕的宽度,按照比例、间距等关系来等比获取,即:

每个Cell的高宽 = (屏幕宽度 - 间距宽度 * 间距数量) / (Cell的数量)

又由于5张和7张有两种size,因此,5张的前两张是一种高度,后三张又是另外一种高度,7张同理,即可得到以下关系:

(kHobenNinePhotoViewSpacing即每个Cell之间的间距,这里取了5.f)

+ (CGFloat) getImageWidthWithWidth:(CGFloat)width index:(NSInteger)i count:(NSInteger) cnt {
    CGFloat spacing = kHobenNinePhotoViewSpacing;
    if (cnt == 2) {
        return (width - spacing) / 2;
    } else if (cnt % 3 == 0 || cnt == 4) {
        return (width - 2 * spacing) / 3;
    } else if (cnt == 8) {
        return (width - 3 * spacing) / 4;
    } else if (cnt == 5) {
        if (i <= 1) {
            return (width - spacing) / 2;
        } else {
            return (width - 2 * spacing) / 3;
        }
    } else if (cnt == 7) {
        if (i <= 2) {
            return (width - 2 * spacing) / 3;
        } else {
            return (width - 3 * spacing) / 4;
        }
    } else
        return 200.f;
}

在设置size的函数直接调用这个函数获取高宽就OK了:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    NSInteger currentItem = 0;
    if (self.colorArray.count == 5)
        currentItem = 2 * indexPath.section + indexPath.item;
    else if (self.colorArray.count == 7)
        currentItem = 3 * indexPath.section + indexPath.item;
    else
        currentItem = indexPath.item;

    CGFloat width = [HobenNinePhotoView getImageWidthWithWidth: self.frame.size.width index: currentItem count: self.colorArray.count];
    
    return CGSizeMake(width, width);
}

由于5张和7张的情况是分成了两个部分的,因此,还需要调用UICollectionViewDelegateFlowLayout的委托来设置两个部分之间的间距:

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 0, kHobenNinePhotoViewSpacing, 0);
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return kHobenNinePhotoViewSpacing;
}

最后,初始化InfoCell中的CollectionView,记得注册什么的:

- (UICollectionView *)ninePhotoView {
    if (!_ninePhotoView) {
        _ninePhotoView = ({
            UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
            [layout setMinimumLineSpacing:2.f];
            [layout setMinimumInteritemSpacing:2.f];
            [layout setScrollDirection:UICollectionViewScrollDirectionVertical];
            [layout setSectionInset:UIEdgeInsetsZero];
            UICollectionView *view = [[UICollectionView alloc] initWithFrame: self.bounds
                                                        collectionViewLayout: layout];
            view.dataSource = self;
            view.delegate = self;
            view.alwaysBounceVertical = YES;
            view.scrollEnabled = NO;
            view.backgroundColor = [UIColor clearColor];
            [view registerClass: [HobenNinePhotoCell class] forCellWithReuseIdentifier: HobenNinePhotoID];
            view;
        });
    }
    return _ninePhotoView;
}

2. PhotoCell

PhotoCell即票圈布局里面的每一个照片,这时候,其实只要适配InfoCell给他设置好的高宽,还有内容就OK了:

- (void)layoutSubviews {
    [self.gridView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(self);
        make.top.equalTo(self);
        make.left.equalTo(self);
    }];
}

- (void) setColor:(UIColor *)color {
    self.gridView.backgroundColor = color;
}

3.Controller

Controller里面包含InfoCell,是用UITableView来包含的,但是如何获得每一个TableView的高度是一个难题,因为每一种布局里面的高度都是不固定的,如果计算高度发生错误,就会导致整个布局出现bug!

先初始化,设置委托,注册一下:

- (UITableView *)infoView {
    if (!_infoView) {
        _infoView = ({
            UITableView *tableView = [[UITableView alloc] initWithFrame: CGRectMake(0, 0,
                                                                               self.view.frame.size.width,
                                                                               self.view.frame.size.height)];
            [tableView registerClass: [HobenInfoCell class] forCellReuseIdentifier: HobenInfoCellID];
            tableView.delegate = self;
            tableView.dataSource = self;
            tableView;
        });
    }
    return _infoView;
}

这里的TableView就不用像Cell里面的CollectionView一样要分区处理了,直接按照数组数量来搞定!

#pragma mark - <UITableViewDelegate>
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.colorArrayArray.count;
}

好的,又是一个关键的地方,怎么获得高度?这是一个值得思考的地方,想想之前是怎么获得的?参考一下,就可以知道,由于:

高度=宽度

每个Cell的高度 = (屏幕宽度 - 间距宽度 * 间距数量) / (Cell的数量)

TableView高度 = 每行Cell的高度之和

因为5张和7张比较特殊,上面一层的高度和下面一层的高度是不一致的,因此还是要分别加一下,而其他情况则可以直接用行数来获得:

+ (CGFloat) heightWithWidth: (CGFloat) width colorArray: (NSArray *) colorArray {
    CGFloat imageHeight = 0.f;
    if(colorArray.count > 1) {
        if (colorArray.count == 2) {
            imageHeight = (width - kHobenNinePhotoViewSpacing) / 2;
        } else if (colorArray.count % 3 == 0) {
            NSInteger imageLine = colorArray.count / 3;
            imageHeight = imageLine * ((width - 2 * kHobenNinePhotoViewSpacing) / 3 + kHobenNinePhotoViewSpacing);
        } else if (colorArray.count == 4) {
            imageHeight = 2 * width / 3;
        } else if (colorArray.count == 5) {
            imageHeight = (width - kHobenNinePhotoViewSpacing) / 2 + (width - 2 * kHobenNinePhotoViewSpacing) / 3 + kHobenNinePhotoViewSpacing;
        } else if (colorArray.count == 7) {
            imageHeight = (width - 2 * kHobenNinePhotoViewSpacing) / 3 + (width - 3 * kHobenNinePhotoViewSpacing) / 4 + kHobenNinePhotoViewSpacing;
        } else if (colorArray.count == 8) {
            imageHeight = 2 * (width - 3 * kHobenNinePhotoViewSpacing) / 4 + kHobenNinePhotoViewSpacing;
        }
    }
    else {
        return 200.f;
    }
    return imageHeight;
}

在TableView高度确定中调用这个函数就OK啦!

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat height = [HobenNinePhotoView heightWithWidth: self.view.frame.size.width - 20 colorArray: self.colorArrayArray[indexPath.item]];
    return height + 20.f;
}

4.数据的传递

最后特别说一下数据的传递过程,在demo中,数据源的产生在Controller层,那么如何精确地传递给PhotoCell呢?跟着我的思路,来看看从数据的创建到数据的传递吧!

在Controller层先建立一坨坨颜色:

- (NSMutableArray *)colorArrayArray {
    if (!_colorArrayArray) {
        _colorArrayArray = ({
            NSMutableArray *arrays = [[NSMutableArray alloc] init];
            UIColor *black = [UIColor blackColor];
            UIColor *yellow = [UIColor yellowColor];
            UIColor *red = [UIColor redColor];
            UIColor *green = [UIColor greenColor];
            UIColor *gray = [UIColor grayColor];
            UIColor *orange = [UIColor orangeColor];
            UIColor *darkGray = [UIColor darkGrayColor];
            UIColor *brown = [UIColor brownColor];
            UIColor *blue = [UIColor blueColor];
            
            NSArray *totalColorArray = @[black, yellow, red, green, gray, orange, darkGray, brown, blue];
            
            for (int i = 1; i <= 9; i++) {
                NSArray *array = [totalColorArray subarrayWithRange: NSMakeRange(0, i)];
                [arrays addObject: array];
            }
            arrays;
        });
    };
    return _colorArrayArray;
}

没错,他叫colorArrayArray,因为他是数组的数组,即[[1], [1,2], [1, 2, 3]...],在TableView里面传递给每一个Cell的时候,就取出其中一个数组,传递下去:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    HobenInfoCell *cell = [tableView dequeueReusableCellWithIdentifier: HobenInfoCellID forIndexPath: indexPath];
    [cell setColorArray: self.colorArrayArray[indexPath.item]];
    return cell;
}

好的,数组的数组拆成了一个个数组,进入了InfoCell层,InfoCell再把得到的数组传给自己的CollectionView(记得reload):

- (void)setColorArray:(NSArray *)colorArray {
    if (_colorArray != colorArray) {
        _colorArray = colorArray;
        [self.ninePhotoView reloadData];
    }
}

CollectionView获得了数组之后,就可以把它传给PhotoCell啦!

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    HobenNinePhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: HobenNinePhotoID forIndexPath: indexPath];
    
    NSInteger currentItem = 0;
    if (self.colorArray.count == 5)
        currentItem = 2 * indexPath.section + indexPath.item;
    else if (self.colorArray.count == 7)
        currentItem = 3 * indexPath.section + indexPath.item;
    else
        currentItem = indexPath.item;
    
    [cell setColor: [self.colorArray objectAtIndex: currentItem]];
    
    return cell;
}

最后在PhotoCell中,根据获得的颜色,来设置自身的背景就大功告成啦!

- (void) setColor:(UIColor *)color {
    self.gridView.backgroundColor = color;
}

5.效果图

6.Demo地址

我的Demo地址在这里,欢迎各位路过的爷给个star~ 嘻嘻

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271