iOS 如何让UIScrollView的子控件居中算法

前言:

项目页面翻新,原来的那个库功能挺好用,但是样式不对,一时之间不想找新的库去替换,冲冠一怒,把那个库的代码copy了一份,改写了样式。啊,好烦人的样式。

代码地址:https://github.com/gityuency/ObjectiveCTools
示例代码类名 【LetSVSCViewController】

效果图:
点击效果:


点击效果.gif

滑动效果:


滑动效果.gif

新增效果:【子控件 固定宽度、变宽、宽度放大、子控件左右带有间隙】


新的.gif

上代码!

第一节:UIScrollView内部控件是固定宽度的,或者内部控件宽度各不相同。

.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyScrollView : UIScrollView <UIScrollViewDelegate>

- (instancetype)initWithFrameFixedSubViewWidth:(CGRect)frame;

- (instancetype)initWithFrameRandomSubViewWidth:(CGRect)frame;

@end

NS_ASSUME_NONNULL_END

.m

#import "MyScrollView.h"

@interface MyScrollView ()

/// 放入子视图
@property (nonatomic, strong) NSMutableArray <UIView *> *arrayItemViews;

@end

@implementation MyScrollView

- (instancetype)initWithFrameFixedSubViewWidth:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor redColor];
        [self fixedButtonWidth];
        self.delegate = self;
    }
    return self;
}

- (instancetype)initWithFrameRandomSubViewWidth:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor redColor];
        [self randomButtonWidth];
        self.delegate = self;
    }
    return self;
}

/// 变宽 按钮
- (void)randomButtonWidth {
    UIButton *beforeButton;
    _arrayItemViews = [NSMutableArray array];
    for (int i = 0; i < 25; i ++) {
        UIButton *b = [[UIButton alloc] init];
        b.backgroundColor = [self RandomColor];
        [b setTitle:@"变宽按钮" forState:UIControlStateNormal];
        CGFloat w = 50 + (arc4random_uniform(30) / 2) * 10;
        if (beforeButton) {  //第二个按钮开始排
            b.frame = CGRectMake(CGRectGetMaxX(beforeButton.frame), 0, w, self.frame.size.height);
        } else {              //第一个按钮
            b.frame = CGRectMake(0, 0, w, self.frame.size.height);
        }
        beforeButton = b;
        [b addTarget:self action:@selector(actionButton:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:b];
        [_arrayItemViews addObject:b];
    }
    self.contentSize = CGSizeMake(CGRectGetMaxX(beforeButton.frame), self.frame.size.height);
}

/// 定宽 按钮
- (void)fixedButtonWidth {
    CGFloat w = 120;
    _arrayItemViews = [NSMutableArray array];
    for (int i = 0; i < 25; i ++) {
        UIButton *b = [[UIButton alloc] init];
        b.backgroundColor = [self RandomColor];
        [b setTitle:@"定宽按钮" forState:UIControlStateNormal];
        b.frame = CGRectMake(i * w, 0, w, self.frame.size.height);
        [b addTarget:self action:@selector(actionButton:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:b];
        [_arrayItemViews addObject:b];
    }
    self.contentSize = CGSizeMake(w * _arrayItemViews.count, self.frame.size.height);
}


/// 按钮点击事件
- (void)actionButton:(UIButton *)buttonCurrent {
    //设置滚动
    CGFloat scrollViewCenterX = self.frame.size.width * 0.5;
    if (scrollViewCenterX == 0) {  //宽度都没有,啥也不用干了
        return;
    }
    
    if (self.contentSize.width < self.frame.size.width) {
        return;                     //内容宽度小于外部宽度, 啥也不用干了
    }
    
    CGFloat buttonCenterX = buttonCurrent.center.x;
    if (buttonCenterX < scrollViewCenterX) {
        [self setContentOffset:CGPointMake(0, 0) animated:YES];
    } else if (buttonCenterX > scrollViewCenterX) {
        CGFloat unVisiableWidth = self.contentSize.width - self.frame.size.width;
        CGFloat needOffset = buttonCenterX - scrollViewCenterX;
        if (unVisiableWidth > needOffset) {  //剩下的可滑动的区域可以给与偏移
            [self setContentOffset:CGPointMake(needOffset, 0) animated:YES];
        } else {  //剩下的已经不够了, 那么就直接显示
            [self setContentOffset:CGPointMake(unVisiableWidth, 0) animated:YES];
        }
    }
}


- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSLog(@"停止减速 offsetX %f", scrollView.contentOffset.x);
    
    CGFloat offsetX = scrollView.contentOffset.x + self.frame.size.width * 0.5;  // 哪个按钮和这个距离最接近,就放大哪个按钮
    
    UIButton *needLargeButton;
    for (UIButton *b in _arrayItemViews) {
        if ((CGRectGetMinX(b.frame) <= offsetX) && (CGRectGetMaxX(b.frame) >= offsetX)) {
            needLargeButton = b;
            NSLog(@" %@", b.titleLabel.text);
            break;
        }
    }
    [self actionButton:needLargeButton];
}


- (UIColor *)RandomColor {
    UIColor * randomColor= [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
    return randomColor;
}
@end

第二节:UIScrollView内部,选中的控件宽度变大,未选中的宽度缩小。

.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface YouScrollView : UIScrollView <UIScrollViewDelegate>

- (instancetype)initWithFrame:(CGRect)frame;

@end

NS_ASSUME_NONNULL_END

.m

#import "YouScrollView.h"

@interface YouScrollView ()

@property (nonatomic, strong) NSMutableArray <UIView *> *arrayItemViews;

@property (nonatomic, strong) NSArray *titleArray;

@property (nonatomic, strong) NSMutableArray *arraySmallSize;

@property (nonatomic, strong) NSMutableArray *arrayLargeSize;

@property (nonatomic) NSInteger smallFontSize;

@property (nonatomic) NSInteger largeFontSize;

@end

@implementation YouScrollView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor redColor];
        _smallFontSize = 12;
        _largeFontSize = 26;
        [self randomButtonWidth];
        self.delegate = self;
    }
    return self;
}

/// 变宽 按钮
- (void)randomButtonWidth {
    
    _arrayItemViews = [NSMutableArray array];
    
    _titleArray = @[@"按钮宽度动态放大", @"小米锅巴", @"茶π", @"冰糖雪梨", @"汇源牌肾宝", @"昆仑山", @"鲜之每日C", @"REKORDERLIG瑞可德林", @"王老吉", @"RedBull红牛", @"健力宝", @"宝矿力水特", @"维维", @"Gatorade佳得乐", @"黑牛", @"东鹏特饮", @"Starbucks星巴克", @"Maxwell麦斯威尔", @"HOGOOD后谷咖啡", @"五粮液", @"古井贡酒", @"永和豆浆", @"南方黑芝麻", @"Soyspring冰泉", @"锐澳RIO"];
    
    //先算一遍大小尺寸
    _arraySmallSize = [NSMutableArray array];
    _arrayLargeSize = [NSMutableArray array];
    for (NSString *s in _titleArray) {
        CGSize sizeS = [self needSize:s fontSize:_smallFontSize];
        CGSize sizeL = [self needSize:s fontSize:_largeFontSize];
        [_arraySmallSize addObject:@(sizeS)];
        [_arrayLargeSize addObject:@(sizeL)];
    }
    
    NSLog(@"%@  %@", _arraySmallSize, _arrayLargeSize);
    
    UIButton *beforeButton;
    for (int i = 0; i < _titleArray.count; i ++) {
        UIButton *b = [[UIButton alloc] init];
        b.backgroundColor = [self RandomColor];
        NSString *s = _titleArray[i];
        [b setTitle:s forState:UIControlStateNormal];
        //[b setTitleColor:[self RandomColor] forState:UIControlStateNormal];
        if (beforeButton) {  //第二个按钮开始排
            CGSize size = [_arraySmallSize[i] CGSizeValue];
            b.frame = CGRectMake(CGRectGetMaxX(beforeButton.frame), 0, size.width, self.frame.size.height);
            b.titleLabel.font = [UIFont systemFontOfSize:_smallFontSize];
        } else {              //第一个按钮
            CGSize size = [_arrayLargeSize[i] CGSizeValue];
            b.frame = CGRectMake(0, 0, size.width, self.frame.size.height);
            b.titleLabel.font = [UIFont systemFontOfSize:_largeFontSize];
        }
        beforeButton = b;
        [b addTarget:self action:@selector(actionButton:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:b];
        [_arrayItemViews addObject:b];
    }
    self.contentSize = CGSizeMake(CGRectGetMaxX(beforeButton.frame), self.frame.size.height);
}


/// 计算尺寸
- (CGSize)needSize:(NSString *)title fontSize:(CGFloat)fontSize {
    CGSize size = [title sizeWithAttributes:@{NSFontAttributeName : [UIFont boldSystemFontOfSize:fontSize]}];
    size.width += 20; //左右两边留 10 白
    return size;
}

/// 按钮点击事件
- (void)actionButton:(UIButton *)buttonCurrent {
    
    UIButton *beforeButton;
    for (UIButton *b in _arrayItemViews) {
        if (b == buttonCurrent) {
            NSUInteger index = [_arrayItemViews indexOfObject:b];
            CGSize size = [_arrayLargeSize[index] CGSizeValue];
            b.titleLabel.font = [UIFont systemFontOfSize:_largeFontSize];
            [UIView animateWithDuration:0.2 animations:^{
                b.frame = CGRectMake(CGRectGetMaxX(beforeButton.frame), 0, size.width, self.frame.size.height);
            }];
        } else {
            NSUInteger index = [_arrayItemViews indexOfObject:b];
            CGSize size = [_arraySmallSize[index] CGSizeValue];
            b.titleLabel.font = [UIFont systemFontOfSize:_smallFontSize];
            [UIView animateWithDuration:0.2 animations:^{
                b.frame = CGRectMake(CGRectGetMaxX(beforeButton.frame), 0, size.width, self.frame.size.height);
            }];
        }
        beforeButton = b;
    }
    
    self.contentSize = CGSizeMake(CGRectGetMaxX(beforeButton.frame), self.frame.size.height);
    
    //设置滚动
    CGFloat scrollViewCenterX = self.frame.size.width * 0.5;
    if (scrollViewCenterX == 0) {
        return;
    }
    if (self.contentSize.width < self.frame.size.width) {
        return;
    }
    
    CGFloat buttonCenterX = buttonCurrent.center.x;
    if (buttonCenterX < scrollViewCenterX) {
        [self setContentOffset:CGPointMake(0, 0) animated:YES];
    } else if (buttonCenterX > scrollViewCenterX) {
        CGFloat unVisiableWidth = self.contentSize.width - self.frame.size.width;
        CGFloat needOffset = buttonCenterX - scrollViewCenterX;
        if (unVisiableWidth > needOffset) {  //剩下的可滑动的区域可以给与偏移
            [self setContentOffset:CGPointMake(needOffset, 0) animated:YES];
        } else {  //剩下的已经不够了, 那么就直接显示
            [self setContentOffset:CGPointMake(unVisiableWidth, 0) animated:YES];
        }
    }
}

/// 代理
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSLog(@"停止减速 offsetX %f", scrollView.contentOffset.x);
    
    CGFloat offsetX = scrollView.contentOffset.x + self.frame.size.width * 0.5;  // 哪个按钮和这个距离最接近,就放大哪个按钮
    
    UIButton *needLargeButton;
    for (UIButton *b in _arrayItemViews) {
        if ((CGRectGetMinX(b.frame) <= offsetX) && (CGRectGetMaxX(b.frame) >= offsetX)) {
            needLargeButton = b;
            NSLog(@" %@", b.titleLabel.text);
            break;
        }
    }
    [self actionButton:needLargeButton];
}


- (UIColor *)RandomColor {
    UIColor * randomColor= [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
    return randomColor;
}

@end

在控制器里使用

.m

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CGFloat sw = [UIScreen mainScreen].bounds.size.width;
    CGFloat sh = [UIScreen mainScreen].bounds.size.height;
    
    MyScrollView *scrollView1 = [[MyScrollView alloc] initWithFrameFixedSubViewWidth:CGRectMake(0, 100, sw, 80)];
    [self.view addSubview:scrollView1];
    
    MyScrollView *scrollView2 = [[MyScrollView alloc] initWithFrameRandomSubViewWidth:CGRectMake(0, 220, sw, 80)];
    [self.view addSubview:scrollView2];
    
    YouScrollView *scrollView3 = [[YouScrollView alloc] initWithFrame:CGRectMake(0, 350, sw, 80)];
    [self.view addSubview:scrollView3];
    
    CGFloat x = (sw + 1) / 2;
    UIView *line = [[UIView alloc] initWithFrame:CGRectMake(x, 0, 1, sh)];
    line.backgroundColor = [UIColor blackColor];
    [self.view addSubview:line];
}

@end

结语:

改个库也能花了我一个周末。不改还总觉得心里不痛快。为了杜绝公司UI大姐及产品挑刺,还是狠心葬送掉了美好的周末。周末就算不打游戏,躺着睡觉不也挺好么。

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