iOS开发——UI篇OC篇&UIDynamic详解
iOS开发拓展篇—UIDynamic(简单介绍)
一、简单介绍
1.什么是UIDynamic
UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架
可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象
如:重力、弹性碰撞等现象
2.物理引擎的价值
广泛用于游戏开发,经典成功案例是“愤怒的小鸟”
让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果
提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏
3.知名的2D物理引擎
Box2d
Chipmunk
二、使用步骤
要想使用UIDynamic来实现物理仿真效果,大致的步骤如下
(1)创建一个物理仿真器(顺便设置仿真范围)
(2)创建相应的物理仿真行为(顺便添加物理仿真元素)
(3)将物理仿真行为添加到物理仿真器中 开始仿真
三、相关说明
1.三个概念
(1)谁要进行物理仿真?
物理仿真元素(Dynamic Item)
(2)执行怎样的物理仿真效果?怎样的动画效果?
物理仿真行为(Dynamic Behavior)
(3)让物理仿真元素执行具体的物理仿真行为
物理仿真器(Dynamic Animator)
2.物理仿真元素
注意:
不是任何对象都能做物理仿真元素
不是任何对象都能进行物理仿真
物理仿真元素要素:
任何遵守了UIDynamicItem协议的对象
UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真
UICollectionViewLayoutAttributes类默认也遵守UIDynamicItem协议
3.物理仿真行为
(1)UIDynamic提供了以下几种物理仿真行为
UIGravityBehavior:重力行为
UICollisionBehavior:碰撞行为
UISnapBehavior:捕捉行为
UIPushBehavior:推动行为
UIAttachmentBehavior:附着行为
UIDynamicItemBehavior:动力元素行为
(2)物理仿真行为须知
上述所有物理仿真行为都继承自UIDynamicBehavior
所有的UIDynamicBehavior都可以独立进行
组合使用多种行为时,可以实现一些比较复杂的效果
4.物理仿真器
(1)物理仿真器须知
它可以让物理仿真元素执行物理仿真行为
它是UIDynamicAnimator类型的对象
(2)UIDynamicAnimator的初始化
- (instancetype)initWithReferenceView:(UIView *)view;
view参数:是一个参照视图,表示物理仿真的范围
5.物理仿真器的说明
(1)UIDynamicAnimator的常见方法
- (void)addBehavior:(UIDynamicBehavior *)behavior; //添加1个物理仿真行为
- (void)removeBehavior:(UIDynamicBehavior *)behavior; //移除1个物理仿真行为
- (void)removeAllBehaviors; //移除之前添加过的所有物理仿真行为
(2)UIDynamicAnimator的常见属性
@property (nonatomic, readonly) UIView* referenceView; //参照视图
@property (nonatomic, readonly, copy) NSArray* behaviors;//添加到物理仿真器中的所有物理仿真行为
@property (nonatomic, readonly, getter = isRunning) BOOL running;//是否正在进行物理仿真
@property (nonatomic, assign) id <UIDynamicAnimatorDelegate> delegate;//代理对象(能监听物理仿真器的仿真过程,比如开始和结束)
由于博客迁移至www.coderyi.com,文章请看http://www.coderyi.com/archives/426
UIkit动力学是UIkit框架中模拟真实世界的一些特性。
UIDynamicAnimator
主要有UIDynamicAnimator类,通过这个类中的不同行为来实现一些动态特性。
它一般有两种初始化方法,先讲常见的第一种
animator= [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
动态特性的实现主要依靠它所添加的行为,通过以下方法进行添加和移除,
[animator removeAllBehaviors];
接下来介绍五个不同的行为,UIAttachmentBehavior(吸附),UICollisionBehavior(碰撞),UIGravityBehavior(重力),UIPushBehavior(推动),UISnapBehavior(捕捉)。另外还有一个辅助的行为UIDynamicItemBehavior,用来在item层级设定一些参数,比如item的摩擦,阻力,角阻力,弹性密度和可允许的旋转等等。
UIAttachmentBehavior(吸附)
先讲吸附行为,
它的初始化方法
attachmentBehavior = attachedToAnchor:location];
item是实现UIDynamicItem协议的id类型,这里设置吸附一个UIImageView的实例iv。offset可以设置吸附的偏移,anchor是设置锚点。
UIAttachmentBehavior有几个属性,例如damping,frequency。damping是阻尼数值,frequency是震动频率
直接上代码,实现一个pan手势,让一个image跟着手势跑
-(void)gesture:(UIPanGestureRecognizer *)gesture{
CGPoint location = [gesture locationInView:self.view];
CGPoint boxLocation = [gesture locationInView:iv];
switch (gesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(@"you touch started position %@",NSStringFromCGPoint(location));
NSLog(@"location in image started is %@",NSStringFromCGPoint(boxLocation));
[animator removeAllBehaviors];
// Create an attachment binding the anchor point (the finger's current location)
// to a certain position on the view (the offset)
UIOffset centerOffset = UIOffsetMake(boxLocation.x - CGRectGetMidX(iv.bounds),
boxLocation.y - CGRectGetMidY(iv.bounds));
attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:iv
offsetFromCenter:centerOffset
attachedToAnchor:location];
attachmentBehavior.damping=0.5;
attachmentBehavior.frequency=0.8;
// Tell the animator to use this attachment behavior
[animator addBehavior:attachmentBehavior];
break;
}
case UIGestureRecognizerStateEnded: {
[animator removeBehavior:attachmentBehavior];
break;
}
default:
[attachmentBehavior setAnchorPoint:[gesture locationInView:self.view]];
break;
}
}
UIPushBehavior(推动)
UIPushBehavior 可以为一个UIView施加一个力的作用,这个力可以是持续的,也可以只是一个冲量。我们可以指定力的大小,方向和作用点等等信息。
pushBehavior = mode:UIPushBehaviorModeInstantaneous];
UIPushBehavior 有pushDirection、magnitude等属性,
CGPoint velocity = [gesture velocityInView:self.view];
CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
if (magnitude > ThrowingThreshold) {
pushBehavior = [[UIPushBehavior alloc]
initWithItems:@[iv]
mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = CGVectorMake((velocity.x / ) , (velocity.y / ));
pushBehavior.magnitude = magnitude / ThrowingvelocityPadding;
[animator addBehavior:pushBehavior];
//3
// UIDynamicItemBehavior 其实是一个辅助的行为,用来在item层级设定一些参数,比如item的摩擦,阻力,角阻力,弹性密度和可允许的旋转等等
NSInteger angle = arc4random_uniform() - ;
itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[iv]];
itemBehavior.friction = 0.2;
itemBehavior.allowsRotation = YES;
[itemBehavior addAngularVelocity:angle forItem:iv];
[animator addBehavior:itemBehavior];
[self performSelector:@selector(resetDemo) withObject:nil afterDelay:0.4];
}
UIGravityBehavior(重力)
直接上代码,实现随机掉落一张图片的代码
// Set up
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
self.gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:nil];
[self.animator addBehavior:self.gravityBeahvior];
- (void)tapped:(UITapGestureRecognizer *)gesture {
NSUInteger num = arc4random() % + ;
NSString *filename = [NSString stringWithFormat:@"m%lu", (unsigned long)num];
UIImage *image = [UIImage imageNamed:filename];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
CGPoint tappedPos = [gesture locationInView:gesture.view];
imageView.center = tappedPos;
[self.gravityBeahvior addItem:imageView];
}
UICollisionBehavior(碰撞)
继续上面的代码,当图片快掉落出边界的时候有 碰撞效果,这个就是UICollisionBehavior实现的。
// Set up
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
self.gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:nil];
self.collisionBehavior = [[UICollisionBehavior alloc] initWithItems:nil];
self.collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
self.itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:nil];
self.itemBehavior.elasticity = 0.6;
self.itemBehavior.friction = 0.5;
self.itemBehavior.resistance = 0.5;
[self.animator addBehavior:self.gravityBeahvior];
[self.animator addBehavior:self.collisionBehavior];
[self.animator addBehavior:self.itemBehavior];
- (void)tapped:(UITapGestureRecognizer *)gesture {
NSUInteger num = arc4random() % + ;
NSString *filename = [NSString stringWithFormat:@"m%lu", (unsigned long)num];
UIImage *image = [UIImage imageNamed:filename];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
CGPoint tappedPos = [gesture locationInView:gesture.view];
imageView.center = tappedPos;
[self.gravityBeahvior addItem:imageView];
[self.collisionBehavior addItem:imageView];
[self.itemBehavior addItem:imageView];
}
另外,UICollisionBehavior有它的代理,其中列举两个方法,它们表示行为开始和结束的时候的代理。
- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier atPoint:(CGPoint)p; - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier;
UISnapBehavior(捕捉)
UISnapBehavior 将UIView通过动画吸附到某个点上。
- (void) handleTap:(UITapGestureRecognizer *)paramTap{
CGPoint tapPoint = [paramTap locationInView:self.view];
if (self.snapBehavior != nil){
[self.animator removeBehavior:self.snapBehavior];
}
self.snapBehavior = [[UISnapBehavior alloc] initWithItem:self.squareView snapToPoint:tapPoint];
self.snapBehavior.damping = 0.5f; //剧列程度
[self.animator addBehavior:self.snapBehavior];
}
UICollectionView与UIDynamicAnimator
文章开头说到UIDynamicAnimator有两种初始化方法,这里介绍它与UICollectionView的完美结合,让UICollectionView产生各种动态特性的行为。
你是否记得iOS系统中信息应用中的附有弹性的消息列表,他就是加入了UIAttachmentBehavior吸附行为,这里通过一个UICollectionView实现类似效果。
主要是复写UICollectionViewFlowLayout,在layout中为每一个布局属性元素加上吸附行为就可以了。
关于复写layout,可以参考onevcat的博客
http://www.onevcat.com/2012/08/advanced-collection-view/
下面就直接上代码了
首先遍历每个 collection view layout attribute 来创建和添加新的 dynamic animator
-(void)prepareLayout {
[super prepareLayout];
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self];
CGSize contentSize = [self collectionViewContentSize];
NSArray *items = [super layoutAttributesForElementsInRect:CGRectMake(, , contentSize.width, contentSize.height)];
for (UICollectionViewLayoutAttributes *item in items) {
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:item.center];
attachment.length = ;
attachment.damping = self.damping;
attachment.frequency = self.frequency;
[_animator addBehavior:attachment];
}
}
}
接下来我们现在需要实现 layoutAttributesForElementsInRect: 和 layoutAttributesForItemAtIndexPath: 这两个方法,UIKit 会调用它们来询问 collection view 每一个 item 的布局信息。我们写的代码会把这些查询交给专门做这些事的 dynamic animator
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
return [_animator itemsInRect:rect];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return [_animator layoutAttributesForCellAtIndexPath:indexPath];
}
然后是响应滚动事件的方法
这个方法会在 collection view 的 bound 发生改变的时候被调用,根据最新的 content offset 调整我们的 dynamic animator 中的 behaviors 的参数。在重新调整这些 behavior 的 item 之后,我们在这个方法中返回 NO;因为 dynamic animator 会关心 layout 的无效问题,所以在这种情况下,它不需要去主动使其无效
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
UIScrollView *scrollView = self.collectionView;
CGFloat scrollDelta = newBounds.origin.y - scrollView.bounds.origin.y;
NSLog(@" %f %f",newBounds.origin.y,scrollView.bounds.origin.y);
CGPoint touchLocation = [scrollView.panGestureRecognizer locationInView:scrollView];
for (UIAttachmentBehavior *behavior in _animator.behaviors) {
CGPoint anchorPoint = behavior.anchorPoint;
CGFloat distanceFromTouch = fabsf(touchLocation.y - anchorPoint.y);
CGFloat scrollResistance = distanceFromTouch / self.resistanceFactor;
UICollectionViewLayoutAttributes *item = [behavior.items firstObject];
CGPoint center = item.center;
center.y += (scrollDelta > ) ? MIN(scrollDelta, scrollDelta * scrollResistance)
: MAX(scrollDelta, scrollDelta * scrollResistance);
item.center = center;
[_animator updateItemUsingCurrentState:item];
}
return NO;
}
让我们仔细查看这个代码的细节。首先我们得到了这个 scroll view(就是我们的 collection view ),然后计算它的 content offset 中 y 的变化(在这个例子中,我们的 collection view 是垂直滑动的)。一旦我们得到这个增量,我们需要得到用户接触的位置。这是非常重要的,因为我们希望离接触位置比较近的那些物体能移动地更迅速些,而离接触位置比较远的那些物体则应该滞后些。
对于 dynamic animator 中的每个 behavior,我们将接触点到该 behavior 物体的 y 的距离除以 500。分母越小,这个 collection view 的的交互就越有弹簧的感觉。
iOS开发——UI篇OC篇&UIDynamic详解的更多相关文章
- iOS开发——UI精选OC篇&UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍
UIApplication,UIWindow,UIViewController,UIView(layer)简单介绍 一:UIApplication:单例(关于单例后面的文章中会详细介绍,你现在只要知道 ...
- iOS开发——UI高级OC篇&自定义控件之调整按钮中子控件(图片和文字)的位置
自定义控件之调整按钮中子控件(图片和文字)的位置 其实还有一种是在storyBoard中实现的,只需要设置对应空间的左右间距: 这里实现前面两种自定义的方式 一:imageRectForContent ...
- iOS开发——网络实用技术OC篇&网络爬虫-使用青花瓷抓取网络数据
网络爬虫-使用青花瓷抓取网络数据 由于最近在研究网络爬虫相关技术,刚好看到一篇的的搬了过来! 望谅解..... 写本文的契机主要是前段时间有次用青花瓷抓包有一步忘了,在网上查了半天也没找到写的完整的教 ...
- ios开发——实用技术篇OC篇&iOS的主要框架
iOS的主要框架 阅读目录 Foundation框架为所有的应用程序提供基本系统服务 UIKit框架提供创建基于触摸用户界面的类 Core Data框架管着理应用程序数据模型 Core ...
- iOS开发——高级技术OC篇&运行时(Runtime)机制
运行时(Runtime)机制 本文将会以笔者个人的小小研究为例总结一下关于iOS开发中运行时的使用和常用方法的介绍,关于跟多运行时相关技术请查看笔者之前写的运行时高级用法及相关语法或者查看响应官方文档 ...
- iOS开发——运行时OC篇&使用运行时获取系统的属性:使用自己的手势修改系统自带的手势
使用运行时获取系统的属性:使用自己的手势修改系统自带的手势 有的时候我需要实现一个功能,但是没有想到很好的方法或者想到了方法只是那个方法实现起来太麻烦,一或者确实为了装逼,我们就会想到iOS开发中最牛 ...
- iOS开发——网络实用技术OC篇&网络爬虫-使用java语言抓取网络数据
网络爬虫-使用java语言抓取网络数据 前提:熟悉java语法(能看懂就行) 准备阶段:从网页中获取html代码 实战阶段:将对应的html代码使用java语言解析出来,最后保存到plist文件 上一 ...
- iOS 开发之 -- UDID和UUID的详解
老实说,搞了几年的ios开发了,对基础的概念,还是不牢固,整天都是为了赶进度而码代码,这里记录一下这两者的区别: UDID的全名为 Unique Device Identifier :设备唯一标识符. ...
- iOS开发——高级技术&本地化与国际化详解
本地化与国际化详解 效果如下: 英语: 中文: 具体实现如下: ...
- iOS开发——新特性OC篇&IOS9 SDK新特性
iOS9 SDK新特性 WWDC 2015苹果开发者大会是移动开发者一年一度的盛会,InfoQ中文站除了第一时间整理Keynote内容分享给大家之外,还邀请了资深的一线开发者分享他们的收获.本文为王巍 ...
随机推荐
- SCOI2010 and SXOI2014 股票交易(DP)
明显的单调队列…… 但下面的程序一直有bug 附上题解:http://blog.csdn.net/njlcazl/article/details/8611042 附上我的代码: var head,ta ...
- apache开源项目--log4j
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件.甚至是套接口服务 器.NT的事件记录器.UNIX Syslog守护进程等; ...
- (五)学习CSS之line-height属性
参考:http://www.jb51.net/w3school/css/pr_dim_line-height.htm line-height 属性设置行间的距离(行高). 注释:不允许使用负值. 值 ...
- Button 自定义(一)-shape
需求:自定义Button,使用系统自定义Shape: 效果图: 1.默认状态 2.选中状态 实现分析: 1.目录结构: 代码实现: 1.button_normal.xml <?xml versi ...
- 从linux启动到rootfs的挂载分析
简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统.在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系 ...
- 通过history.pushState无刷新改变url
通过history.pushState无刷新改变url 背景 在浏览器中改变地址栏url,将会触发页面资源的重新加载,这使得我们可以在不同的页面间进行跳转,得以浏览不同的内容.但随着单页应用的增多,越 ...
- 34、Android中基于Socket的网络通信(一)
Socket又称”套接字”,应用程序通常通过”套接字”向网络发出请求或者应答网络请求. 在java中,Socket和ServerSocket类库位于java.net包中,ServerSocket用于服 ...
- codeforces 671C Ultimate Weirdness of an Array 线段树+构造
题解上说的很清楚了,我照着写的,表示膜拜题解 然后时间复杂度我觉得应该是O(nlogn),虽然常数略大,预处理和倒着扫,都是O(nlogn) #include <stdio.h> #inc ...
- win7安装gevent时报错 whl is not a supported wheel on this platform.
1.首先强烈推荐一个站点 在使用pip安装python协程包gevent时,需要很多依赖,很多需要编译的底层支持等等,不能拿来就用.总之很多麻烦的事儿. 这个强烈推荐一个站点,里面都是一些编译好的py ...
- IE兼容性问题解决方案1--ajax请求不发送到后台
相信很多小伙伴会遇到这种问题,用ajax做异步请求的时候,在IE浏览器下,并没有发送出去.但是相关程序确实执行了.为什么呢? 原来这是IE缓存方式的原因,所以呢,用下边的解决方案吧. 1.在请求的UR ...