Masonry复杂ScrollView布局
前言
说到iOS
自动布局,有很多的解决办法。有的人使用xib/storyboard
自动布局,也有人使用frame
来适配。对于前者,笔者并不喜欢,也不支持。对于后者,更是麻烦,到处计算高度、宽度等,千万大量代码的冗余,对维护和开发的效率都很低。
笔者在这里介绍纯代码自动布局的第三方库:Masonry
。这个库使用率相当高,在全世界都有大量的开发者在使用,其star
数量也是相当高的。
效果图
本节详解Masonry
的循环创建视图,并且可以展开与收缩的用法,先看看效果图:
当我们点击某一行时,可以展开:
核心代码
看下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
@interface ScrollViewComplexController ()
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) NSMutableArray *expandStates;
@end
@implementation ScrollViewComplexController
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.pagingEnabled = NO;
[self.view addSubview:self.scrollView];
self.scrollView.backgroundColor = [UIColor lightGrayColor];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
UILabel *lastLabel = nil;
for (NSUInteger i = 0; i < 10; ++i) {
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0;
label.layer.borderColor = [UIColor greenColor].CGColor;
label.layer.borderWidth = 2.0;
label.text = [self randomText];
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
[label addGestureRecognizer:tap];
// We must preferredMaxLayoutWidth property for adapting to iOS6.0
label.preferredMaxLayoutWidth = screenWidth - 30;
label.textAlignment = NSTextAlignmentLeft;
label.textColor = [self randomColor];
[self.scrollView addSubview:label];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(15);
make.right.mas_equalTo(self.view).offset(-15);
if (lastLabel) {
make.top.mas_equalTo(lastLabel.mas_bottom).offset(20);
} else {
make.top.mas_equalTo(self.scrollView).offset(20);
}
make.height.mas_equalTo(40);
}];
lastLabel = label;
[self.expandStates addObject:[@[label, @(NO)] mutableCopy]];
}
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self.view);
// 让scrollview的contentSize随着内容的增多而变化
make.bottom.mas_equalTo(lastLabel.mas_bottom).offset(20);
}];
}
- (NSMutableArray *)expandStates {
if (_expandStates == nil) {
_expandStates = [[NSMutableArray alloc] init];
}
return _expandStates;
}
- (UIColor *)randomColor {
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1];
}
- (NSString *)randomText {
CGFloat length = arc4random() % 150 + 5;
NSMutableString *str = [[NSMutableString alloc] init];
for (NSUInteger i = 0; i < length; ++i) {
[str appendString:@"测试数据很长,"];
}
return str;
}
- (void)onTap:(UITapGestureRecognizer *)sender {
UIView *tapView = sender.view;
NSUInteger index = 0;
for (NSMutableArray *array in self.expandStates) {
UILabel *view = [array firstObject];
if (view == tapView) {
NSNumber *state = [array lastObject];
if ([state boolValue] == YES) {
[view mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(40);
}];
} else {
UIView *preView = nil;
UIView *nextView = nil;
if (index - 1 < self.expandStates.count && index >= 1) {
preView = [[self.expandStates objectAtIndex:index - 1] firstObject];
}
if (index + 1 < self.expandStates.count) {
nextView = [[self.expandStates objectAtIndex:index + 1] firstObject];
}
[view mas_remakeConstraints:^(MASConstraintMaker *make) {
if (preView) {
make.top.mas_equalTo(preView.mas_bottom).offset(20);
} else {
make.top.mas_equalTo(20);
}
make.left.mas_equalTo(15);
make.right.mas_equalTo(self.view).offset(-15);
}];
if (nextView) {
[nextView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(view.mas_bottom).offset(20);
}];
}
}
[array replaceObjectAtIndex:1 withObject:@(!state.boolValue)];
[self.view setNeedsUpdateConstraints];
[self.view updateConstraintsIfNeeded];
[UIView animateWithDuration:0.35 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
break;
}
index++;
}
}
@end
|
讲解
当我们要收起的时候,只是简单地设置其高度的约束为40
,但是当我们要展开时,实现起来就相对麻烦了。因为我们需要重新添加约束,要重新给所点击的视图添加约束,就需要知道前一个依赖视图和后一个依赖视图的约束,以便将相关联的都更新约束。
当我们更新所点击的视图时,我们通过判断是否有前一个依赖视图来设置顶部约束:
1
2
3
4
5
6
7
|
if (preView) {
make.top.mas_equalTo(preView.mas_bottom).offset(20);
} else {
make.top.mas_equalTo(20);
}
|
除了这个之外,我们也需要更新后一个视图的约束,因为我们对所点击的视图调用了mas_remakeConstraints
方法,就会移除其之前所添加的所有约束,所以我们必须重新将后者对当前点击的视图的依赖重新添加上去:
1
2
3
4
5
6
7
|
if (nextView) {
[nextView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(view.mas_bottom).offset(20);
}];
}
|
Masonry复杂ScrollView布局的更多相关文章
- 使用Masonry搭建特殊布局时与xib的对比
之前只有比较浅的接触过Masonry.项目中大多数的布局还是用xib中的AutoLayout与手码的frame计算相结合,相信也会有很多项目和我一样是这两种布局的组合.其实xib各方面用的感觉都挺好, ...
- android中RelativeLayout无法填充ScrollView布局的问题
ScrollView是解决布局过长的情况下使用,一遍其下面会有个顶部布局,我项目里面是RelativeLayout,但是RelativeLayout无论设置 android:layout_height ...
- 详解嵌套ListView、ScrollView布局显示不全的问题
在项目开发中,可能经常遇到嵌套ListView.ScrollView的问题,就是重写onMeasure方法.解决如下 public class ExpandListView extends ListV ...
- iOS masonry 不规则tagView布局 并自适应高度
在搜索页面经常会有不规则的tag出现,这种tagView要有点击事件,单个tagView可以设置文字颜色,宽度不固定根据内容自适应,高度固定,数量不固定.总高度就不固定.最近对于masonry的使用又 ...
- ScrollView不设置contentSize属性依然也可以作为底层滚动View(使用masonry设置scrollView的contentSize)
第一步 //下层的scroolView self.baseScrollView = [[UIScrollView alloc] init]; self.baseScrollView.delegate ...
- Masonry 布局 scrollView
原理 scrollView的高度(纵向滑动时)时靠内部的子控件撑起来的.我们直接给ScrollView布局会发现失败.用层级检查器发现,ScrollVIiw的高度有问题,我们可以选择添加一个UIVie ...
- IOS控件布局之Masonry布局框架
前言: 回想起2013年做iOS开发的时候,那时候并没有采用手写布局代码的方式,而是采用xib文件来编写,如果使用纯代码方式是基于window的size(320,480)计算出一个相对位置进行布局,那 ...
- iOS学习——布局利器Masonry框架源码深度剖析
iOS开发过程中很大一部分内容就是界面布局和跳转,iOS的布局方式也经历了 显式坐标定位方式 --> autoresizingMask --> iOS 6.0推出的自动布局(Auto La ...
- Scrollview包裹布局问题。
输入框获取焦点,键盘弹出,背景图片上移: https://blog.csdn.net/wljian1/article/details/79962802 android:scrollbarThumbVe ...
随机推荐
- .NET下 JSON 的一些常用操作
1.JSON的序列化和反序列化 Newtonsoft.Json dll 下载地址http://json.codeplex.com/ using System; using System.Collect ...
- 【组合 数学】codeforces C. Do you want a date?
codeforces.com/contest/810/problem/C [题意] 给定一个集合A,求 , 输入: [思路] 基数为n的集合有2^n-1个非空子集. 首先n个数要从小到大排序,枚举最后 ...
- 获取当前日期的T-SQL语句
CONVERT(nvarchar(10),count_time,121): CONVERT为日期转换函数,一般就是在时间类型 (datetime,smalldatetime)与字符串类型(nchar, ...
- Swift--字典的了解
字典存储时,key和value值的类型都是固定的,且都是无序的. 1.字典类型的缩写语法 在swift中,字典的完整格式如下: Dictionary<Key, Value> 注意:字典的k ...
- python学习之-- shelve模块
shelve模块定义:是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle(因为封装了pickle)可支持的python数据格式.功能:可以实现pickle的多次dump和多次 ...
- 51 Nod 1244 莫比乌斯函数前n项和
积性函数前n项和必看好文 https://blog.csdn.net/skywalkert/article/details/50500009 递归计算的时候要用map记忆化一下,前面的打表会比较快一点 ...
- codevs——1269 匈牙利游戏
1269 匈牙利游戏 2012年CCC加拿大高中生信息学奥赛 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Descript ...
- The Bottom of a Graph
poj——The Bottom of a Graph Time Limit: 3000MS Memory Limit: 65536K ...
- BZOJ4555求和(cdq分治+NTT)
题意: 输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果.1 ≤ n ≤ 100000 其中S(i,j)是第二类Stirling数,即有i个球,丢到j个盒子中,要求盒子不 ...
- MD5加密Java工具类
原文:http://www.open-open.com/code/view/1421764946296 import java.security.MessageDigest; public class ...