QQ粘性布局

*:first-child {
margin-top: 0 !important; }
body > *:last-child {
margin-bottom: 0 !important; }

a {
color: #4183C4; }
a.absent {
color: #cc0000; }
a.anchor {
display: block;
padding-left: 30px;
margin-left: -30px;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
bottom: 0; }

h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
cursor: text;
position: relative; }

h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
text-decoration: none; }

h1 tt, h1 code {
font-size: inherit; }

h2 tt, h2 code {
font-size: inherit; }

h3 tt, h3 code {
font-size: inherit; }

h4 tt, h4 code {
font-size: inherit; }

h5 tt, h5 code {
font-size: inherit; }

h6 tt, h6 code {
font-size: inherit; }

h1 {
font-size: 28px;
color: black; }

h2 {
font-size: 24px;
border-bottom: 1px solid #cccccc;
color: black; }

h3 {
font-size: 18px; }

h4 {
font-size: 16px; }

h5 {
font-size: 14px; }

h6 {
color: #777777;
font-size: 14px; }

p, blockquote, ul, ol, dl, li, table, pre {
margin: 15px 0; }

hr {
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
border: 0 none;
color: #cccccc;
height: 4px;
padding: 0;
}

body > h2:first-child {
margin-top: 0;
padding-top: 0; }
body > h1:first-child {
margin-top: 0;
padding-top: 0; }
body > h1:first-child + h2 {
margin-top: 0;
padding-top: 0; }
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
margin-top: 0;
padding-top: 0; }

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0; }

h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
margin-top: 0; }

li p.first {
display: inline-block; }
li {
margin: 0; }
ul, ol {
padding-left: 30px; }

ul :first-child, ol :first-child {
margin-top: 0; }

dl {
padding: 0; }
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px; }
dl dt:first-child {
padding: 0; }
dl dt > :first-child {
margin-top: 0; }
dl dt > :last-child {
margin-bottom: 0; }
dl dd {
margin: 0 0 15px;
padding: 0 15px; }
dl dd > :first-child {
margin-top: 0; }
dl dd > :last-child {
margin-bottom: 0; }

blockquote {
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }

table {
padding: 0;border-collapse: collapse; }
table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
table tr:nth-child(2n) {
background-color: #f8f8f8; }
table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
table tr th :first-child, table tr td :first-child {
margin-top: 0; }
table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }

img {
max-width: 100%; }

span.frame {
display: block;
overflow: hidden; }
span.frame > span {
border: 1px solid #dddddd;
display: block;
float: left;
overflow: hidden;
margin: 13px 0 0;
padding: 7px;
width: auto; }
span.frame span img {
display: block;
float: left; }
span.frame span span {
clear: both;
color: #333333;
display: block;
padding: 5px 0 0; }
span.align-center {
display: block;
overflow: hidden;
clear: both; }
span.align-center > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: center; }
span.align-center span img {
margin: 0 auto;
text-align: center; }
span.align-right {
display: block;
overflow: hidden;
clear: both; }
span.align-right > span {
display: block;
overflow: hidden;
margin: 13px 0 0;
text-align: right; }
span.align-right span img {
margin: 0;
text-align: right; }
span.float-left {
display: block;
margin-right: 13px;
overflow: hidden;
float: left; }
span.float-left span {
margin: 13px 0 0; }
span.float-right {
display: block;
margin-left: 13px;
overflow: hidden;
float: right; }
span.float-right > span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: right; }

code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px; }

pre code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent; }

.highlight pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px; }

pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px; }
pre code, pre tt {
background-color: transparent;
border: none; }

sup {
font-size: 0.83em;
vertical-align: super;
line-height: 0;
}
* {
-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
body {
width: 854px;
margin:0 auto;
}
}
@media print {
table, pre {
page-break-inside: avoid;
}
pre {
word-wrap: break-word;
}
}
-->

05-QQ粘性布局

整体思路径:

手指移动,按钮跟着移动.按钮跟着手指移动.移动时底部有一个圆.
根据上面的大圆按钮拖动的距离,小圆的半径在变小.移动时中间有一块不规则的填充区域.
手指移动超出一定的范围,填充效果消失,当手指松开时.判断当前大圆距离与小圆之间的距离.
如果小于60就让大圆回来原来的位置.下次拖动时,同样具有填充效果.
如果大于60,手指松开时,播放一个动画.动画完成时, 删除动画按钮.

实现步骤:

1.自定义大圆控件(UIButton)可以显示背景图片和文字

按钮定义的时候要在初始方法中,把它的基本属性设置好.在开始加载的时候设置.
基本属性包括颜色,圆角,文字颜色,大小. 实现代码:
self.backgroundColor = [UIColor redColor];
self.layer.cornerRadius = self.bounds.size.width * 0.5; self.titleLabel.font = [UIFont systemFontOfSize:12];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];

2.让大圆控件随着手指移动而移动

添加手势.同样也是在初始化方法当中进行设置.
注意不能根据形变修改大圆的位置,只能通过center,因为全程都需要用到中心点计算.
tansform并没有修改center,它修改的是Frame. 添加手势代码为:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self addGestureRecognizer:pan]; 手势实现方法为:
CGPoint transP = [pan translationInView:self]; CGPoint center = self.center;
center.x += transP.x;
center.y +=transP.y;
self.center = center;
注意要做复位,相对于上一次.
[pan setTranslation:CGPointZero inView:self]; 3.在拖动的时候,添加一个小圆控件在原来大圆控件的位置
在初始化方法中添加小圆
注意:添加小圆时不能够直接添加在当前按钮上,因为按钮是可以移动的,如果直接添加在按钮,它会跟着按钮一起移动.
所以把小圆添加到按钮父控件当中.添加时注意,要把小圆添加到按钮底部.不然会把按钮给盖起来.
UIView *smallCircle = [[UIView alloc] init];
smallCircle.frame = self.frame;
smallCircle.layer.cornerRadius = self.layer.cornerRadius;
smallCircle.backgroundColor = self.backgroundColor; self.smallCircle = smallCircle;
[self.superview insertSubview:smallCircle belowSubview:self];
当手指拖动大圆时,小圆的半径会根据拖动的距离进行减小.所以要计算出两个圆之间的距

离. 计算完毕后.让小圆的原始半径每次都减去一个距离比例.重新设置尺寸大小和小圆的半径.

计算两个圆之间距离是一个功能单独抽出来.
方法为:
- (CGFloat)distanceWithSmallCircle:(UIView *)smallView bigCircle (UIView *)bigCircle{
X轴的偏移量
CGFloat offsetX = bigCircle.center.x - smallView.center.x;
Y轴的偏移量
CGFloat offsetY = bigCircle.center.y - smallView.center.y;
CGFloat distance = sqrt(offsetX * offsetX + offsetY * offsetY); return distance;
} 在手指拖动方法计算两个圆之间的距离, 根据拖动的距离让小圆的半径增大减小. 实现代码为:
CGFloat distance = [self distanceWithSmallCircle:self.smallCircle bigCircle:self];
取出小圆的半径
注意这里是取出小圆最初的宽度,由于每次拖动的时候都会去修改小圆的宽 .所以这里不能直接用小圆的宽度
这里用的是大圆的宽度,开始小圆和大圆的宽度是一样的.
大圆在移动时,大圆的宽高没有发现变化,所以可以拿到大圆的宽高
CGFloat smallR = self.bounds.size.width * 0.5;
让小圆的半径每次减去一个距离比例
smallR = smallR - distance / 10.0;
每次移动时,重新设置小圆的宽高
self.smallCircle.bounds = CGRectMake(0, 0, smallR * 2, smallR * 2);
重新设置小圆的圆角
self.smallCircle.layer.cornerRadius = smallR;

4.添加粘性效果

中间的粘性效果其实就是一块填充区域.只要把这个填充区域的路径给求出来就行了.
中间的路径通过确定6个点.把这些点连接出来就行. 求点为:
x1,y1分别是小圆的圆心
x2,y2分别是大圆的圆心
r1代表小圆的半径
r2代表大圆的半径
d是两个圆之间的距离 y轴的偏移量 / 两个圆之间的距离
cosθ = (y2 - y1) / d;
x轴的偏移量 / 两个圆之间的距离
sinθ = (x2 - x1) / d; 已知一个角,一个斜边
角的邻边 = 斜边 * cosθ
角的对边 = 斜边 * sinθ
CGPoint pointA = CGPointMake(x1 - r1 * cosθ, y1 + r1 * sinθ);
CGPoint pointB = CGPointMake(x1 + r1 * cosθ, y1 - r1 * sinθ);
CGPoint pointC = CGPointMake(x2 + r2 * cosθ, y2 - r2 * sinθ);
CGPoint pointD = CGPointMake(x2 - r2 * cosθ, y2 + r2 * sinθ);
CGPoint pointO = CGPointMake(pointA.x + d * 0.5 * sinθ, pointA.y + d * 0.5 * cosθ);
CGPoint pointP = CGPointMake(pointB.x + d * 0.5 * sinθ, pointB.y + d * 0.5 * cosθ); 创建路径,把这些点连接到一起
UIBezierPath *path = [UIBezierPath bezierPath];
AB
[path moveToPoint:pointA];
[path addLineToPoint:pointB];
BC(曲线)
[path addQuadCurveToPoint:pointC controlPoint:pointP];
CD
[path addLineToPoint:pointD];
DA(曲线)
[path addQuadCurveToPoint:pointA controlPoint:pointO];
以上是根据两个圆求出不规则的矩形
求出路径后,要把路径填充起来.但是不能够直接给填充到当前的按钮之上.按钮是可以拖动的.
绘制东西,当超出它的范围以外就不会再绘制.
所以要把路径添加到按钮的子控件当中, 但是当前是一个路径,是不能够直接添加到子控件当中的.
可能过形状图层添加.
形状图层会根据一个路径生成一个形状.把这个形状添加到当前控件的图片父层就可以了. 添加时需要注意:
形状图层之有一个,所以不能够在手指拖动方法当中添加.由于当手指拖动的距离超过某个范围后,形状图片会被移除.
下一次再去移动时, 还会有填充的路径.所以把创建形状图层搞成一个懒加载的形式. 如果发现下一次被删除时,再重新创建. 形式为:
-(CAShapeLayer *)shap{ if (_shap == nil) {
创建形状图层
CAShapeLayer *shap = [CAShapeLayer layer];
设置形状图层的填充颜
shap.fillColor = [UIColor redColor].CGColor;
self.shap = shap;
把形状图层添加到当前按钮的父层当中.
[self.superview.layer insertSublayer:shap atIndex:0];
_shap = shap;
}
return _shap;
}
在手指移动方法当中,给形状图层赋值路径就可以了.

5.粘性业务逻辑处理

在手指移动方法判断两个圆之间的距离, 如果发现两个圆之间的距离超过60时
让底部的小圆隐藏.把路径移除
当小圆显示的时候才绘制填充路径
if (self.smallCircle.hidden == NO) {
UIBezierPath *path = [self pathWithSmallCircle:self.smallCircle bigCircle:self];
self.shap.path = path.CGPath; }
当两个圆之间的距离超过60时.
if(distance > 60){
移除填充路径
[self.shap removeFromSuperlayer];
让底部的小圆隐匿
self.smallCircle.hidden = YES;
}

6.手指停止拖动业务逻辑

移动后手指松开时判断两个圆之间的距离,如果两个圆之间的距离小于60时,让大圆复位.小圆显示.
手指松开时,如果两个圆之间的距离大于60时.播放一个动画.动画播放完毕时.把当前按钮从父控件当中移除. 播放一个动画.
创建一个UIImageView,尺 寸和当前按钮一样大.
UIImageView *imageV = [[UIImageView alloc] initWithFrame:self.bounds];
创建动画图
NSMutableArray *imageArray = [NSMutableArray array];
for (int i = 0; i < 8 ; i++) {
NSString *imageName = [NSString stringWithFormat:@"%d",i+1]; UIImage *image = [UIImage imageNamed:imageName];
[imageArray addObject:image];
} 设置动画图片数组
imageV.animationImages = imageArray;
设置动画执行时长
imageV.animationDuration = 1;
开始动画
[imageV startAnimating];
把UIImageView添加到当前按钮上
[self addSubview:imageV];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self removeFromSuperview]; }); 注意:
在控制器加载完毕后,要取消Autoresizing转自动布局
不然会出现按钮回原位的情况.
self.view.translatesAutoresizingMaskIntoConstraints = NO;
上一篇:iOS 与 惯性滚动


下一篇:Educational Codeforces Round 7 E. Ants in Leaves 贪心