为了让用户在用你的app时有一种身临其境的感觉,UIKit Dynamics and Motion Effects可以帮你实现。
先介绍这两个tools;
UIKit Dynamics: UIKit dynamics是一个集成在UIkit中的full physics engine.用它来通过添加behaviors 比如gravity,collision等,来使用户感到interface十分的真实
可以选择interface elements元素所采纳的physics 特性,dynamics engine 照顾来做其他的事情
Motion Effects: 用Motion Effects可以创建十分cool的视差效果.
UIKit Dynamics与Motion Effects是十分强大的增强用户体验的工具,赋予digital interface生命。
getting start
打开Xcode,创建一个single View application,project name是DynamicsPlayground.创建工程以后,打开ViewController.m文件,在viewDidLoad方法下面添加以下代码:
UIView *square = [[UIView alloc[initWithFrame:CGRectMake(100,100,100,100);
square.backgroundColor = [UIColor grayColor];
[self.view addSubView:square]
以上代码添加在viewDidLoad方法里面,使得程序在load view的时候,在interface上添加了一个UIView :square
Adding gravity
在ViewController.m下,添加如下的property:
@property(strong)·UIDynamicAnimator *animator; //declare a UIDynamicAnimator,as a physics engine
@property(strong) UIGravityBehavior *gravity;
添加下面的代码到viewDidLoad里面:
self.animator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view]; //init the animator, set reference view is self.view
self.gravity = [[UIGravityBehavior alloc]initWithItems:@[square]; // give gravity behavior add items
[self.animator addBehavior:self.gravity]
此时运行app,你会看到square慢慢的加速移动一直到脱离screen的底部边界。
UIDynamicAnimator 是UIKit physics engine,这个类追踪各种各样的添加到physics engine的行为,比如gravity,并且为这些behavior提供整个context.在初始化animator时候,传给他一个reference view,animator用这个reference view定义他自己的坐标系统,即以reference view的frame.original为坐标原点,size.width作为X轴,size.height作为Y轴
UIGravityBehavior 通过对一个或多个items施加force来模拟重力的行为,当创建一个gravity behavior时,通常要把items与gravity behavior联系起来,这些被选择的items将会受到gravity的影响
大部分behavior都允许去配置behavior property. gravity behavior可以配置他的angle和magnitude,angle决定方向,magnitude默认1.0,代表1000 points/s^2,是他的加速度,这个值越大,在相同的时间内运行的距离(points)就越大(多)。可以用0.5*g*t^2来计算下降的points,g is magnitude
setting boundaries
在运行时,square一直在向下移动,为了让square在接触到边界之后就停止运动,这就需要定义一个boundary
添加property: UICollisionbehavior *collision;
在viewDidLoad里面初始化并把它添加到animator里面
self.collision = [[UICollisionBehavior alloc]initWithItems:@[Square]
self.collision.translatesReferenceBoundsIntoBoundary = YES ; // 设定collision boundary is screen
[self.animator addBehavior:self.collision]
run ,会发现square将停留在screen 底部.
##################################################################
UICollisionBehavior class reference
############################
You can add multiple collision behaviors to a dynamic animator. A dynamic item can be part of any number of collision behaviors, provided those behaviors belong to the same animator. For example, you can specify a collision behavior for a set of say, blue, items and another for, say, pink items. When you add both behaviors to a dynamic animator, blue items can collide with each other and pink items can collide with each other, but a blue item and a pink item would not collide—they would ignore each other.
#########################
这段话是说,一个animator可以添加多个collision behaviors,只有在同一个collision behavior的items才会互相发生collision behavior,不同的collision behaviors的items不会发生collision behavior,在默认情况下
获取collision behavior的boundary identifier,调用property boundaryIdentifier readonly return array
property: collisionDelegate 对collision 的响应的掌控
Property:CollisionMode UICollisionBehaviorModeEverything item之间发生collision,在指定的边界也发生collision
UICollisionBehaviorModeBoundaries items的collision仅仅发生在指定的边界处
UICollisionBehaviorModeItems items的collision仅仅发生在items之间
property(readOnly) NSArray items 返回collision behaviors的items
property Bool translatesReferenceBoundsIntoBoundary=YES 设置reference view 作为collision behavior的边界
Instance Methods:
- (void)addBoundaryWithIdentifier:(id<NSCopying>)identifier forPath:(UIBezierPath *)bezierPath
- (void)addBoundaryWithIdentifier:(id<NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2
- (void)addItem:(id<UIDynamicItem>)
item
- (UIBezierPath *)boundaryWithIdentifier:(id<NSCopying>)identifier
Returns a specified Bezier-path boundary.
- (instancetype)initWithItems:(NSArray *)items
- (void)removeAllBoundaries
- (void)removeBoundaryWithIdentifier:(id<NSCopying>)identifier
- (void)removeItem:(id<UIDynamicItem>)item
####################################################################################################
*************************************************************************************************************************************
UIBezierPath class reference
*******************************
用UIBezierPath可以定义由直的或弯的线段组成的path,path既能定义简单的shape,如椭圆,矩形,弧形,也能定义复杂的多边形
画出BezierPath所表示的几何学图像,首先要掌控the path's current point.在创建一个empty bezierPath对象时,the current point是不确定的,必须明确的指定。如果仅仅想移动current point 而不会画出line segment,用方法Move to point:.其他的方法都会画出line segments或curve segment,画的时候开始点是current point,结束在end point,end point是特别指定的,当添加画好的segment到path之后,该segment的end point将自动的变成current point
一个path可以包含任意的subpath, 用closePath可以使得open path变成close path.
moveToPoint设定先一个segment的current point
在current graphics context下,画出路径用方法stroke和fill,stroke画出path轮廓,fill来填充path所围的封闭区域
Property:
@Property(nonatomic,readOnly) CGRect bounds; 代表包含path所有点的最小的矩形区域
@property(nonatomic,readonly) CGPoint currentPoint 代表start point,如果path是空的,currentPoint 是CGpointZero
@Property(readonly,getter = isEmpty) BooL empty 判断一个segment是否是可用的
@flatness(nonatomic) CGFloat flatness 可以精确的设定curve segment的弯曲度factor
Class methods
bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:
画一段圆弧,返回值是bezierPath,clockwise =YES,设定为顺时针
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
用在指定的矩形区域内的椭圆创建一个bezierPath
+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect
用指定的矩形区域创建一个bezierPath
+ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii
在指定的矩形区域创建一个bezierPath a rounded rectangular
UIRectCorner
The corners of a rectangle.
enum {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0
};
typedef NSUInteger UIRectCorner;
Instance Method
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2:
Appends a cubic Bézier curve to the receiver’s path
- (void)addLineToPoint:(CGPoint)point
Appends a straight line to the receiver’s path
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint
- (void)appendPath:(UIBezierPath *)bezierPath
CGAffineTransform
A structure for holding an affine transformation matrix.
struct CGAffineTransform {
CGFloat a;
CGFloat b;
CGFloat c;
CGFloat d;
CGFloat tx;
CGFloat ty;
};
typedef struct CGAffineTransform CGAffineTransform;
Fields
a
-
The entry at position [1,1] in the matrix.
b
-
The entry at position [1,2] in the matrix.
c
-
The entry at position [2,1] in the matrix.
d
-
The entry at position [2,2] in the matrix.
tx
-
The entry at position [3,1] in the matrix.
ty
-
The entry at position [3,2] in the matrix.
Discussion
In Quartz 2D, an affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context. The CGAffineTransform
type provides functions for creating, concatenating, and applying affine transformations.
In Quartz, affine transforms are represented by a 3 by 3 matrix:
Because the third column is always (0,0,1)
, the CGAffineTransform
data structure contains values for only the first two columns.
Conceptually, a Quartz affine transform multiplies a row vector representing each point (x,y) in your drawing by this matrix, producing a vector that represents the corresponding point (x’,y’):
Given the 3 by 3 matrix, Quartz uses the following equations to transform a point (x, y) in one coordinate system into a resultant point (x’,y’) in another coordinate system.
********************************************************************************************************************
Handing Collisions
下面将添加一个不可移动的barrier,与square碰撞和交互
在viewDidLoad下加入:
UIView *barrier = [[UIView alloc[initWithFrame:CGRectMake(0,300,130,20)];
barrier.backgroundColor = [UIColor redColor];
[self.view addSubView:barrier];
run,将会看到barrier出现在screen的一半的地方,但是不能够与square进行交互
animator仅仅对于behaviors相联系的views才会产生影响
Making objects respond to collisions
在viewDidLoad里面用下面的代码代替self.collision的初始化
self.collision = [[UICollisionBehavior alloc]initWithItems:@[square,barrier]];
collision对象应该知道他所交互的每一个view,因此需要把barrier加入items中
run,会看到对象square和barrier可以进行collision交互,但barrier对象会移动
barrier是不可移动的,因此dynamic animator不需要检测到她的存在
Invisible boundaries and collisions
viewDidLoad里设置
self.collision = [[UICollisionBehavior alloc]initWithItems:@[square]];
然后,添加一个boundary如下: boundary为barrier的上边界
CGPoint rightEdge = CGPointMake(barrier.frame.origin.x + barrier.frame.size.width,barrier.frame.origin.y);
[self.collision addBoundaryWithIdentifier:@1 fromPoint:barrier.origin toPoint:rightEdge];
上面添加了添加了一个让dynamic animator看得到的boundary,但barrier对dynamic animator来说是看不见的
run,可以看到square碰撞到barrier的上边界之后被弹开,然后继续向下移动
Behind the scenes of collisions
每一个dynamic behavior都有一个action property,action property提供一个block,animation的每一步都会调用action去执行action block里的代码
添加代码在viewDidLoad中:
self.collision.action = ^{
NSLog(@"%@, %@", NSStringFromCGAffineTransform(square.transform),NSStringFromCGPoint(square.center));
}
collision 发生之后,dynamic animator用squar.transform和square.center去定位view
Collision notifications
当collision发生的时候,如果采纳了UICollisionBehaviorDelegate协议,会调用该协议的方法:
在viewDidLoad里面,在collision behavior 初始化之后,设置view controller为该collision的delegate
self.collision.collisionDelegate = self;
添加colisionBehaviorDelegate的实现方法
-(void)collisionBehavior:(UiCollisionBehavior*)behavior
beganContactForItem:(id<UIDynamicItem> item
withBoundaryIdentifier:(id<NSCopying>identifier
atPoint:(CGPoint) {
NSLog(@"Boundary conatct occured - %@",identifier);
}
当collision发生的时候,该方法被激活.
添加代码到该方法:
UIView *view = (UIView*)item;
view.backgroundColor =[UIColor yellowColor];
[UIView animateWithDuration:0.3
animations:^{
view.backgroundColor =[UIColor grayColor];
}];
Configuring item properties 可以配置的item property包括item碰撞时的mass和弹性
在viewDidLoad的末尾添加:
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc]initWithItems:@[square];
itemBehavior.elasicity = 0.6; //1.0 没有任何能量的损耗,
[self.animator addBehavior:itemBehavior];
还可以添加一个attach behavior
UIAttachmentBehavior *attach = [[UIAttachmentBehavior alloc]initWithItem:view attachedToItem:square];
[self.animator addBehavior:attach]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
UIDynaicItemBehavior class reference
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Property
allowsRotation
Specifies whether rotation is allowed for the behavior’s dynamic items.
@property(readwrite, nonatomic) BOOL allowsRotation 设置item是否可以旋转
angularResistance
The angular resistance for the behavior’s dynamic items.
@property(readwrite, nonatomic)
CGFloat angularResistance 设置旋转的角度阻抗,值越大,角度衰减越大,旋转的越快,停止就越慢
density
The relative mass density of the behavior’s dynamic items.
@property(readwrite, nonatomic)
CGFloat density 设置item的密度,与item的size组合可以算出item的质量
elasticity
The amount of elasticity applied to collisions for the behavior’s dynamic items.
@property(readwrite, nonatomic)
CGFloat elasticity 1.0表示没有能量和速度的损耗
friction
The linear resistance for the behavior’s dynamic items when two slide against each other.
@property(readwrite, nonatomic)
CGFloat friction 线性阻抗 0.0 无摩擦,1.0 strong 摩擦 在平面上滑行时的阻抗
items
Returns the set of dynamic items you’ve added to the dynamic item behavior. (read-only)
@property(nonatomic, readonly, copy)
NSArray *items
resistance
The linear resistance for the behavior’s dynamic items, which reduces their linear velocity over time.
@property(readwrite, nonatomic)
CGFloat resistance
Instance Method
addAngularVelocity:forItem:
Adds a specified angular velocity to a dynamic item.
- (void)addAngularVelocity:(
CGFloat)
velocity forItem:(id<UIDynamicItem>)
item
Parameters
velocity
-
The angular velocity, expressed in radians per second, that you want to add to the specified dynamic item. Default value is 0
. Applying a negative value reduces the angular velocity by the specified amount.
item
-
The dynamic item whose angular velocity you want to increase (or decrease).
addItem:
Adds a dynamic item to the dynamic item behavior’s item array.
- (void)addItem:(id<UIDynamicItem>)
item
addLinearVelocity:forItem:
Adds a specified linear velocity to a dynamic item.
- (void)addLinearVelocity:(
CGPoint)
velocity forItem:(id<UIDynamicItem>)
item
Parameters
velocity
-
The linear velocity, expressed in points per second, that you want to add to the specified dynamic item. Default value is 0
. Applying a negative value reduces the linear velocity by the specified amount.
item
-
The dynamic item whose linear velocity you want to increase (or decrease).
angularVelocityForItem:
Returns the angular velocity for a specified dynamic item.
- (
CGFloat)angularVelocityForItem:(id<UIDynamicItem>)
item
initWithItems:
Initializes a dynamic item behavior with an array of dynamic items.
- (instancetype)initWithItems:(
NSArray *)
items
linearVelocityForItem:
Returns the linear velocity for a specified dynamic item.
- (
CGPoint)linearVelocityForItem:(id<UIDynamicItem>)
item
removeItem:
Removes a specific dynamic item from the dynamic item behavior.
- (void)removeItem:(id<UIDynamicItem>)
item
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UIAttachmentBehavior class reference
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UIAttachmentBehavior指明了两个dynamic items之间的动态连接,或者是一个dynamic item和anchor point之间的连接。
默认情况下,一个item的附着点是该item的center,但可以手动改
Dynamic item 是一个遵循UIDynamicItem protocol的对象
要使Dynamic item移动,可以通过gesture或别的input
@property(readwrite, nonatomic) CGPoint anchorPoint
UIAttachmentBehaviorType
The type of an attachment behavior, indicating what a dynamic item is attached to.
typedef enum {
UIAttachmentBehaviorTypeItems,
UIAttachmentBehaviorTypeAnchor
} UIAttachmentBehaviorType
The amount of damping to apply to the attachment behavior.
@property(readwrite, nonatomic)
CGFloat damping
frequency
The frequency of oscillation for the attachment behavior.
@property(readwrite, nonatomic)
CGFloat frequency
items
The dynamic items connected by the attachment behavior. (read-only)
@property(nonatomic, readonly, copy)
NSArray *items
length
The distance, in points, between the two attachment points of the attachment behavior.
@property(readwrite, nonatomic)
CGFloat length
Instance Method
- (instancetype)initWithItem:(id<UIDynamicItem>)item attachedToAnchor:(CGPoint)point
Parameters
item
-
The dynamic item to which you are applying an attachment behavior.
point
-
The anchor point for the attachment behavior, relative to the coordinate system for the behavior’s associated dynamic animator. For more information, see the Overview in UIDynamicAnimator Class Reference.
initWithItem:attachedToItem:
Initializes an attachment behavior that connects the center point of a dynamic item to the center point of another dynamic item.
- (instancetype)initWithItem:(id<UIDynamicItem>)
item1 attachedToItem:(id<UIDynamicItem>)
item2
- (
instancetype
)initWithItem:(id<UIDynamicItem>)
item
offsetFromCenter:(
UIOffset
)
p1
attachedToAnchor:(
CGPoint
)
point
- (instancetype)initWithItem:(id<UIDynamicItem>)item1 offsetFromCenter:(UIOffset)p1 attachedToItem:(id<UIDynamicItem>)item2 offsetFromCenter:(UIOffset)p2
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^