最近在工作中用到了UIButton的排布,需求是根据button的不同宽度(高度相同)实现自动排布。这是第四次用到了,前面分别写了3个项目,用到了三次,感觉还是比较常用的小功能;第一次自己写了个算法,第二次和三次写成固定宽高的按钮规则排布,这次由于要适应不同宽度的需要,在原有基础上做了修改,可以适应不同的按钮宽度,布局自动计算。
目录
- 实现效果
- 基本思路
- 核心代码
- 附录固定列数的button布局
- 效果图
- 核心代码
实现效果
基本思路
核心问题是根据button的数量,和每一个button的宽度(宽度由文字多少决定)计算button的排布,button高度和内间距可以设为固定常数。
1. 将创建好的按钮放在数组中;
2. 解决分类和计算逻辑:先确定第一个button的位置,然后计算第二个button的位置,第二个button的位置可能在当前行,也可能在下一行。需要两个if else嵌套,在循环遍历button的过程中,动态确定每一个button的位置。
核心代码
1.创建继承于UIView的子类:ButtonLayoutView,在ButtonLayoutView.m中实现计算:
#import "ButtonsLayoutView.h"
CGFloat const buttonH = 30;
CGFloat const buttonInset = 20;
@interface ButtonsLayoutView()
/** button array */
@property (strong, nonatomic) NSMutableArray *buttonArray;
@end
@implementation ButtonsLayoutView
-(NSMutableArray *)buttonArray {
if (!_buttonArray) {
_buttonArray = [NSMutableArray array];
}
return _buttonArray;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self createButtons:@[@"30 g",@"20 g",@"40 g",@"50 g",@"样例样例",@"样例样例样例",@"样例",@"30 g",@"20 g",@"40 g",@"50 g",@"样例样例",@"样例样例样例",@"样例"]];
}
return self;
}
- (void)createButtons:(NSArray *)titles{
for (int i = 0; i < titles.count; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.backgroundColor = [UIColor purpleColor];
[button setTitle:titles[i] forState:UIControlStateNormal];
[self addSubview:button];
// calucate frame
[button sizeToFit];
CGRect rect = button.frame;
rect.size.width += 2 * buttonInset;
rect.size.height = buttonH;
button.frame = rect;
// add array
[self.buttonArray addObject:button];
}
}
- (void)layoutSubviews {
[super layoutSubviews];
for (int i = 0; i < self.buttonArray.count; i ++) {
// 1.take out button
UIButton *button = self.buttonArray[i];
CGRect rect = button.frame;
CGRect superRect = self.frame;
// 2.calucate layout
if (i == 0) { // the first button
rect.origin.x = buttonInset;
rect.origin.y = buttonInset;
} else { // the behind button
UIButton *lastButton = self.buttonArray[i - 1];
CGRect lastRect = lastButton.frame;
CGFloat leftButtonW = CGRectGetMaxX(lastButton.frame) + buttonInset;
// over legnth
CGFloat rightW = superRect.size.width - leftButtonW - buttonInset;
if (rightW < rect.size.width) { // next line
rect.origin.x = buttonInset;
rect.origin.y = CGRectGetMaxY(lastButton.frame) + buttonInset;
} else {// current line
rect.origin.x = leftButtonW;
rect.origin.y = lastRect.origin.y;
}
}
button.frame = rect;
}
// superView Height
UIButton *button = [self.buttonArray lastObject];
CGRect rect = self.frame;
rect.size.height = CGRectGetMaxY(button.frame) + buttonInset;
self.frame = rect;
}
@end
2.测试:在ViewController.m中导入ButtonLayoutView的头文件,开始使用,需要给出ButtonsLayoutView的宽度,和起点坐标,高度会自动被计算:
- (void)viewDidLoad {
[super viewDidLoad];
ButtonsLayoutView *layoutView = [[ButtonsLayoutView alloc]init];
layoutView.backgroundColor = [UIColor brownColor];
CGRect rect = layoutView.frame;
rect.size.width = 300;
rect.origin.x = 50;
rect.origin.y = 50;
layoutView.frame = rect;
layoutView.clipsToBounds = YES;
[self.view addSubview:layoutView];
}
布局">附录:固定列数的button布局
利用固定列计算button布局,带单选功能。
效果图
核心代码
1.定义
#import "ZFLReportAbuse.h"
@interface ZFLReportAbuse()
/** 被选中的button */
@property (strong, nonatomic) UIButton *selectedButton;
@end
@implementation ZFLReportAbuse
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = ZFLRGBColor(30, 30, 30);
NSArray *titleArray = @[@"网络盗图",@"垃圾广告",@"不实信息",@"敏感信息",@"其他",@"人身攻击"];
// 最大列数
int maxCols = 2;
// 根据最大列算最大行数
int maxRows;
if (titleArray.count <= 3) {
maxRows = 1;
} else {
// 整除,除尽
BOOL isDivisible = (titleArray.count % maxCols) == 0;
if (isDivisible) {
maxRows = (int)titleArray.count / maxCols;
} else {
maxRows = (int)titleArray.count / maxCols + 1;
}
}
CGFloat insetMargin = 15;
CGFloat buttonHeight = 30;
for (int i = 0; i < titleArray.count; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:titleArray[i] forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"normal_icon"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"selected_icon"] forState:UIControlStateSelected];
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
button.width = 100;
button.height = buttonHeight;
button.top = insetMargin + (i / maxCols) * button.height + (i / maxCols) * insetMargin;
button.left = insetMargin + (i % maxCols) * button.width + (i % maxCols) * insetMargin;
[button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
// 默认选中第一个
if (i == 0) {
button.selected = YES;
self.selectedButton = button;
}
self.height = (buttonHeight + insetMargin) * maxRows + insetMargin;
self.width = SCREEN_WIDTH;
}
}
return self;
}
- (void)buttonClick:(UIButton *)button {
self.selectedButton.selected = NO;
self.selectedButton = button;
self.selectedButton.selected = YES;
}
@end
2.使用
ZFLReportAbuse *reportView = [[ZFLReportAbuse alloc]init];
reportView.top = 44;
reportView.left = 0;
[self.view addSubview:reportView];