概述
最近群里有人私信我關(guān)于iOS物理引擎的知識,雖然UIDynamic在iOS7就引入了伪窖,但項目中還真沒用到過逸寓,就簡單研究了下。由于本demo很簡單覆山,就沒有上傳GitHub竹伸,想要源碼的可以進(jìn)文章底部的技術(shù)群獲取。
詳細(xì)
一簇宽、基本知識
UIDynamic可以為繼承UIView的控件添加物理行為勋篓。可以看下這些API
Dynamic Animator 動畫者魏割,為動力學(xué)元素提供物理學(xué)相關(guān)的能力及動畫譬嚣,同時為這些元素提供相關(guān)的上下文,是動力學(xué)元素與底層iOS物理引擎之間的中介钞它,將Behavior對象添加到Animator即可實現(xiàn)動力仿真
Dynamic Animator Item:動力學(xué)元素拜银,是任何遵守了UIDynamic協(xié)議的對象,從iOS7開始遭垛,UIView和UICollectionViewLayoutAttributes默認(rèn)實現(xiàn)協(xié)議尼桶,如果自定義對象實現(xiàn)了該協(xié)議,即可通過Dynamic Animator實現(xiàn)物理仿真锯仪。
-
UIDynamicBehavior:仿真行為泵督,是動力學(xué)行為的父類,基本的動力學(xué)行為類包括:
- UIGravityBehavior 重力行為
- UICollisionBehavior 碰撞行為
- UIAttachmentBehavior 吸附行為
- UISnapBehavior 迅猛移動彈跳擺動行為
- UIPushBehavior 推動行為
具體實現(xiàn)步驟:
1庶喜、創(chuàng)建一個仿真者[UIDynamicAnimator] 用來仿真所有的物理行為
2小腊、創(chuàng)建具體的物理仿真行為[如重力UIGravityBehavior]
3、將物理仿真行為添加給仿真者實現(xiàn)仿真效果久窟。
二秩冈、單行為效果
我這里簡單寫幾個行為事例,其他創(chuàng)建方法基本和這一樣瘸羡,可以自己嘗試:
1、重力效果:
// 創(chuàng)建一個仿真者[UIDynamicAnimator] 用來仿真物理行為
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
// 創(chuàng)建重力的物理仿真行為搓茬,并設(shè)置具體的items(需要仿真的view)
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[view]];
// 將重力仿真行為添加給仿真者實現(xiàn)仿真效果犹赖,開始仿真
[animator addBehavior:gravity];
具體效果:
2、碰撞效果:
是不是很簡單卷仑,咱們再來一個碰撞效果:
// 碰撞檢測
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[view]];
// 設(shè)置不要出邊界峻村,碰到邊界會被反彈
collision.translatesReferenceBoundsIntoBoundary = YES;
// 開始仿真
[animator addBehavior:collision];
具體效果:
3、擺動效果:
// 創(chuàng)建震動行為锡凝,snapPoint是它的作用點
self.snap = [[UISnapBehavior alloc] initWithItem:view snapToPoint:view.center];
// 開始仿真
[animator addBehavior:collision];
具體效果:
單效果創(chuàng)建都差不多粘昨,這里只寫幾個簡單的,其他的可以看我參考的這篇文章 http://www.reibang.com/p/e096d2dda478
三、組合行為效果
把單效果稍微組合一下:
1张肾、重力加碰撞:
// 創(chuàng)建一個仿真者[UIDynamicAnimator] 用來仿真物理行為
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
// 創(chuàng)建重力的物理仿真行為芭析,并設(shè)置具體的items(需要仿真的view)
_gravity = [[UIGravityBehavior alloc] init];
_collision = [[UICollisionBehavior alloc] init];
_collision.translatesReferenceBoundsIntoBoundary = YES;
// 將重力仿真行為添加給仿真者實現(xiàn)仿真效果,開始仿真
[self.animator addBehavior:_gravity];
[self.animator addBehavior:_collision];
// 為view添加重力效果
[self.gravity addItem:view];
// 為view添加碰撞效果
[self.collision addItem:view];
具體效果:
2吞瞪、重力加彈跳:
// 動態(tài)媒介
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[self.animators addObject:animator];
// 重力
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[square]];
[animator addBehavior:gravity];
// 碰撞
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[square]];
collision.collisionDelegate = self;
[collision addBoundaryWithIdentifier:@"barrier" forPath:[UIBezierPath bezierPathWithRect:self.view.bounds]];
collision.translatesReferenceBoundsIntoBoundary = YES;
[animator addBehavior:collision];
// 動力學(xué)屬性
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[square]];
itemBehavior.elasticity = 1;
[animator addBehavior:itemBehavior];
具體效果:
四芍秆、大廠用到的實際效果
一些大廠在利用這些效果惯疙,比如蘋果的iMessage消息滾動視覺差效果、百度外賣重力感應(yīng)(這個用到了重力感應(yīng))妖啥、摩拜單車貼紙效果霉颠,接下來咱們就逐個實現(xiàn)一下這些效果:
1、防iMessage滾動效果:
這里參考了著名開發(fā)者王維@onevcat重的一篇文章
// 自定義UICollectionViewFlowLayout
@interface WZBCollectionViewLayout : UICollectionViewFlowLayout
// 重寫prepareLayout方法
- (void)prepareLayout
{
[super prepareLayout];
if (!_animator) {
// 創(chuàng)建一個仿真者[UIDynamicAnimator] 用來仿真物理行為
_animator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self];
CGSize contentSize = [self collectionViewContentSize];
NSArray *items = [super layoutAttributesForElementsInRect:CGRectMake(0, 0, contentSize.width, contentSize.height)];
for (UICollectionViewLayoutAttributes *item in items) {
// 創(chuàng)建一個吸附行為
UIAttachmentBehavior *spring = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:item.center];
spring.length = 0;
spring.damping = .8;
spring.frequency = .5;
[_animator addBehavior:spring];
}
}
}
// 重寫這個方法刷新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
UIScrollView *scrollView = self.collectionView;
CGFloat scrollDeltaY = newBounds.origin.y - scrollView.bounds.origin.y;
CGFloat scrollDeltaX = newBounds.origin.x - scrollView.bounds.origin.x;
CGPoint touchLocation = [scrollView.panGestureRecognizer locationInView:scrollView];
for (UIAttachmentBehavior *spring in _animator.behaviors) {
CGPoint anchorPoint = spring.anchorPoint;
CGFloat distanceFromTouch = fabs(touchLocation.y - anchorPoint.y);
CGFloat scrollResistance = distanceFromTouch / 2000;
UICollectionViewLayoutAttributes *item = (id)[spring.items firstObject];
CGPoint center = item.center;
center.y += (scrollDeltaY > 0) ? MIN(scrollDeltaY, scrollDeltaY * scrollResistance)
: MAX(scrollDeltaY, scrollDeltaY * scrollResistance);
CGFloat distanceFromTouchX = fabs(touchLocation.x - anchorPoint.x);
center.x += (scrollDeltaX > 0) ? MIN(scrollDeltaX, scrollDeltaX * distanceFromTouchX / 2000)
: MAX(scrollDeltaX, scrollDeltaX * distanceFromTouchX / 2000);
item.center = center;
[_animator updateItemUsingCurrentState:item];
}
return NO;
}
具體效果:
2荆虱、防摩拜單車貼紙效果:
// 這里需要創(chuàng)建一個監(jiān)聽運動的管理者用來監(jiān)聽重力蒿偎,然后實時改變重力方向
_motionManager = [[CMMotionManager alloc] init];
// 設(shè)備狀態(tài)更新幀率
_motionManager.deviceMotionUpdateInterval = 0.01;
// 創(chuàng)建view
for (NSInteger i = 0; i < 40; i++) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MobikeTest"]];
imageView.frame = CGRectMake(100, 0, 50, 50);
imageView.layer.masksToBounds = YES;
imageView.layer.cornerRadius = 25;
[self.view addSubview:imageView];
// 添加重力效果
[self.gravity addItem:imageView];
// 碰撞效果
[self.collision addItem:imageView];
self.dynamicItem.elasticity = .7;
// 添加動力學(xué)屬性
[self.dynamicItem addItem:imageView];
}
// 開始監(jiān)聽
[self.motionManager startDeviceMotionUpdatesToQueue:NSOperationQueue.mainQueue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
// 設(shè)置重力方向
self.gravity.gravityDirection = CGVectorMake(motion.gravity.x * 3, -motion.gravity.y * 3);
}];
具體效果:
3、防百度外賣首頁重力感應(yīng):
// 這里需要創(chuàng)建一個監(jiān)聽運動的管理者用來監(jiān)聽重力方向克伊,然后實時改變UI
_motionManager = [[CMMotionManager alloc] init];
// 加速計更新頻率酥郭,我這里設(shè)置每隔0.06s更新一次,也就是說愿吹,每隔0.06s會調(diào)用一次下邊這個監(jiān)聽的block
self.motionManager.accelerometerUpdateInterval = 0.06;
// 開始監(jiān)聽
[self.motionManager startAccelerometerUpdatesToQueue:NSOperationQueue.mainQueue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
// 獲取加速計在x方向上的加速度
CGFloat x = accelerometerData.acceleration.x;
// collectionView的偏移量
CGFloat offSetX = self.collectionView.contentOffset.x;
CGFloat offSetY = self.collectionView.contentOffset.y;
// 動態(tài)修改偏移量
offSetX -= 15 * x;
CGFloat maxOffset = self.collectionView.contentSize.width + 15 - self.view.frame.size.width;
// 判斷最大和最小的偏移量
if (offSetX > maxOffset) {
offSetX = maxOffset;
} else if (offSetX < -15) {
offSetX = -15;
}
// 動畫修改collectionView的偏移量
[UIView animateWithDuration:0.06 animations:^{
[self.collectionView setContentOffset:CGPointMake(offSetX, offSetY) animated:NO];
}];
}];
具體效果:
總結(jié)
- 本篇文章參考了
- 最后幾個效果圖有點失真不从,具體效果可以找我要demo看
- 最近急著招人,平時能擠出的時間也不多犁跪,所以這篇文章寫的比較粗糙椿息,有任何疑問都可以私信我
- 喜歡就點個贊吧
我的更多文章:老司機(jī)帶你飛
請不要吝惜,隨手點個喜歡或者關(guān)注一下吧坷衍!您的支持是我最大的動力??寝优!
您可以關(guān)注我以便及時查看我的最新文章,如果您對本篇文章有任何疑問枫耳,請隨時私信我乏矾。
碼字實在不易,可以請我喝杯咖啡嘛~ 期待的搓搓小手??