iOS使用UICollectionView實現可旋轉菜單

來源:互聯網
上載者:User

先來張效果圖:



之前項目中利用UICollectionView實現了一個圓環形分布的菜單,但不能隨著手勢進行旋轉,正好這兩天放假,參考一些大神的文章,搗鼓出了可以進行旋轉的菜單。下面說一下代碼實現。

1.由於UICollectionVIew是依賴於UICollectionViewFlowLayout進行布局,而UICollectionViewFlowLayout 繼承自 UICollectionViewLayout,所以如果我們想進行個人化布局,可以建立一個UICollectionViewLayout的子類來進行個人化布局。

#import <UIKit/UIKit.h>@interface YMLRotationLayout : UICollectionViewLayout/** 按鈕半徑 */@property (assign, nonatomic) CGFloat itemRadius;/** 按鈕中心相對菜單中心旋轉角度*/@property (assign, nonatomic) CGFloat rotationAngle;@end
#import "YMLRotationLayout.h"@implementation YMLRotationLayout{        NSMutableArray * _attributeAttay;    CGFloat _rLength;    NSInteger _itemCount;}- (void)prepareLayout{    [super prepareLayout];        // 按鈕個數    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];    _attributeAttay = [[NSMutableArray alloc] init];    // 先設定大圓的半徑 取長和寬最短的    CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height) / 2.2;    // 圓心位置    CGPoint center = CGPointMake(self.collectionView.frame.size.width / 2.0, self.collectionView.frame.size.height / 2.0);        _rLength = _itemRadius;        // 設定每個item的大小    for (int idx = 0; idx < _itemCount; idx ++) {                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:0];        UICollectionViewLayoutAttributes * attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];        // 設定item大小        attris.size = CGSizeMake(_rLength, _rLength);                if (_itemCount == 1) {                        attris.center = self.collectionView.center;        } else {                        // 計算每個item的圓心位置            /*             .             . .             .   . r             .     .             .........             */            // 計算每個item中心的座標            // 算出的x,y值還要減去item自身的半徑大小            float x = center.x + cosf(2 * M_PI / _itemCount * idx + _rotationAngle) * (radius - _rLength / 2.0);            float y = center.y + sinf(2 * M_PI / _itemCount * idx + _rotationAngle) * (radius - _rLength / 2.0);                        attris.center = CGPointMake(x, y);        }        [_attributeAttay addObject:attris];    }}// contentSize 的大小- (CGSize)collectionViewContentSize{    return self.collectionView.frame.size;}// cell / header / footer 的frame數組- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{    return _attributeAttay;}@end


2.由於需要監聽滑動手勢以調整布局,所以需要重載UICollectionVIew 事件方法

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{        CGPoint centerPoint = self.center;    UITouch *touch = touches.anyObject;    CGPoint point  = [touch locationInView:self];        CGFloat rLength = sqrt((point.x - centerPoint.x) * (point.x - centerPoint.x) + (point.y - centerPoint.y) * (point.y - centerPoint.y));        // 手勢範圍限制    if (!(rLength <= [self.largeRadius floatValue] && rLength >= [self.smallRadius floatValue])) {        return;    }        [[NSNotificationCenter defaultCenter] postNotificationName:@"touchBegin" object:nil userInfo:@{@"x":[NSString stringWithFormat:@"%f",point.x],@"y":[NSString stringWithFormat:@"%f",point.y]}];}- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{        CGPoint centerPoint = self.center;    UITouch *touch = touches.anyObject;    CGPoint point = [touch locationInView:self];        CGFloat rLength = sqrt((point.x - centerPoint.x) * (point.x - centerPoint.x) + (point.y - centerPoint.y) * (point.y - centerPoint.y));        // 手勢範圍限制    if (!(rLength <= [self.largeRadius floatValue] && rLength >= [self.smallRadius floatValue])) {        return;    }        [[NSNotificationCenter defaultCenter] postNotificationName:@"touchMoving" object:nil userInfo:@{@"x":[NSString stringWithFormat:@"%f",point.x],@"y":[NSString stringWithFormat:@"%f",point.y]}];}

3.上面兩步完成之後,再依據滑動前後手勢的位置,計算滑動角度,重新計算每個item的座標,然後重新布局

#pragma mark -- 按鈕滑動,重新布局// 滑動開始- (void)touchBegin:(NSNotification *)sender{    if (!_rotate) return;    _centerPoint = self.collectionView.center;    NSDictionary *dic = sender.userInfo;    CGPoint point = CGPointMake([dic[@"x"] floatValue], [dic[@"y"] floatValue]);    _lastPoint = point;}// 正在滑動中- (void)touchMoving:(NSNotification *)sender{    if (!_rotate) return;    NSDictionary *dic = sender.userInfo;    CGPoint point = CGPointMake([dic[@"x"] floatValue], [dic[@"y"] floatValue]);        // 以collectionView center為中心計算滑動角度    CGFloat rads = [self angleBetweenFirstLineStart:_centerPoint firstLineEnd:_lastPoint andSecondLineStart:_centerPoint secondLineEnd:point];        if (_lastPoint.x != _centerPoint.x && point.x != _centerPoint.x) {                CGFloat k1 = (_lastPoint.y - _centerPoint.y) / (_lastPoint.x - _centerPoint.x);        CGFloat k2 = (point.y - _centerPoint.y) / (point.x - _centerPoint.x);        if (k2 > k1) {            _totalRads += rads;        } else {            _totalRads -= rads;        }    }        _layout.rotationAngle = _totalRads;    // 重新布局    [_layout invalidateLayout];        // 更新記錄點    _lastPoint = point;}// 兩條直線之間的夾角- (CGFloat)angleBetweenFirstLineStart:(CGPoint)firstLineStart firstLineEnd:(CGPoint)firstLineEnd andSecondLineStart:(CGPoint)secondLineStart secondLineEnd:(CGPoint)secondLineEnd{        CGFloat a1 = firstLineEnd.x - firstLineStart.x;    CGFloat b1 = firstLineEnd.y - firstLineStart.y;    CGFloat a2 = secondLineEnd.x - secondLineStart.x;    CGFloat b2 = secondLineEnd.y - secondLineStart.y;        // 夾角餘弦    double cos = (a1 * a2 + b1 * b2) / (sqrt(pow(a1, 2) + pow(b1, 2)) * sqrt(pow(a2, 2) + pow(b2, 2)));    // 浮點計算結果可能超過1,需要控制    cos = cos > 1 ? 1 : cos;    return acos(cos);}


demo地址:https://github.com/HuberyYang/YMLMenuDemo.git

本文新地址:iOS使用UICollectionView實現可旋轉菜單


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.