【iOS】關於 UICollectionView 的自定義佈局

huangruiz1發表於2020-10-19

閒來沒事,做了個 UICollectionView 的圓形佈局,效果圖如下
Simulator Screen Shot 2017年5月24日 下午5.49.46.png

#控制器程式碼如下

//
//  ViewController.m
//  CollectionViewTest
//
//  Created by 黃瑞 on 2017/5/24.
//  Copyright © 2017年 CoderHuang. All rights reserved.
//

#import "ViewController.h"
#import "MyLayout.h"
#import "MyCollectionView.h"

@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource>
{
    NSInteger count;
}

@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;

// 方塊個數
@property (weak, nonatomic) IBOutlet UISlider *countSlider;

// 起始位置
@property (weak, nonatomic) IBOutlet UISlider *originSlider;

// 方塊個數Label
@property (weak, nonatomic) IBOutlet UILabel *countLabel;

// 顏色陣列
@property (nonatomic, strong) NSArray <UIColor *> *colors;

@end

@implementation ViewController
#pragma mark - View did load
- (void)viewDidLoad {
    [super viewDidLoad];
    count = (NSInteger)self.countSlider.value;
    self.countLabel.text = [NSString stringWithFormat:@"%ld", (long)count];
    self.colors = @[
                    [UIColor redColor],
                    [UIColor orangeColor],
                    [UIColor yellowColor],
                    [UIColor greenColor],
                    [UIColor blueColor],
                    [UIColor magentaColor],
                    [UIColor purpleColor],
                    ];
}

#pragma mark - Slider click
- (IBAction)valueChange:(UISlider *)sender {
    if (sender == self.countSlider) {
        NSInteger nCount = (NSInteger)self.countSlider.value;
        if (nCount != count) {
            count = nCount;
            self.countLabel.text = [NSString stringWithFormat:@"%ld", (long)count];
            [self.collectionView reloadData];
        }
    } else {
        MyLayout *layout = (MyLayout *)self.collectionView.collectionViewLayout;
        layout.origin = sender.value;
        [self.collectionView reloadData];
    }
}

// 和 UITableView 類似,UICollectionView也是有 Section 和 Row 的
#pragma mark - Delegate
#pragma mark - Collection view delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return count;
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    // 已經在 StoryBoard 中設定了ReuseIdentifier
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
    
    cell.backgroundColor = self.colors[indexPath.row % self.colors.count];

    return cell;
}

@end

#StoryBoard
如圖

螢幕快照 2017-05-24 下午5.59.39.png

注意,要將 CollectionView 的 Layout 改為 Custom 然後選擇自定義類 MyLayout,它必須繼承自 UICollectionViewLayout 類。

#MyLayout.h

//
//  MyLayout.h
//  CollectionViewTest
//
//  Created by 黃瑞 on 2017/5/24.
//  Copyright © 2017年 CoderHuang. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface MyLayout : UICollectionViewLayout

// 起始的角度
@property (nonatomic, assign) CGFloat origin;

@end

#MyLayout.m

//
//  MyLayout.m
//  CollectionViewTest
//
//  Created by 黃瑞 on 2017/5/24.
//  Copyright © 2017年 CoderHuang. All rights reserved.
//

#import "MyLayout.h"

@interface MyLayout ()
{
    // item的數量
    NSInteger count;
    
    // 圓形佈局的中心
    CGPoint center;
    
    // 圓形佈局的半徑
    CGFloat radius;
}

@end

@implementation MyLayout
// 重寫父類的四個方法
- (void)prepareLayout {
    count = [self.collectionView numberOfItemsInSection:0];
    center = CGPointMake(self.collectionView.frame.size.width / 2, self.collectionView.frame.size.height / 2);
    radius = 150;
}

// 返回 ContentSize
- (CGSize)collectionViewContentSize {
    return self.collectionView.frame.size;
}

// 對於每一個 Item 都有一個 UICollectionViewLayoutAttributes 物件與它對應,這個物件僅僅包含了Item的佈局資訊。
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *arr = [NSMutableArray array];
    for (NSInteger i = 0; i < count; i++) {
        [arr addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]]];
    }
    return arr;
}

// 根據 indexPath 來建立 Item 的佈局屬性物件
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%s", __func__);
    UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    CGFloat angle = 2 * M_PI / count * indexPath.row + M_PI_2 * self.origin;
    CGFloat x = center.x + cos(angle) * radius;
    CGFloat y = center.y - sin(angle) * radius;
    
    attr.center = CGPointMake(x, y);
    attr.size = CGSizeMake(30, 30);
    
    return attr;
}

@end

相關文章