使用 UIFieldBehaviors 的粘滞角效果
此示例显示了如何实现类似于 FaceTime 的效果,一旦视图进入特定区域,该视图就被吸引到该点,在这种情况下,两个区域是顶部和底部。
https://i.stack.imgur.com/ICuEL.gif
迅速
class ViewController: UIViewController
{
lazy var dynamicAnimator: UIDynamicAnimator =
{
let dynamicAnimator = UIDynamicAnimator(referenceView: self.view)
return dynamicAnimator
}()
lazy var collision: UICollisionBehavior =
{
let collision = UICollisionBehavior(items: [self.orangeView])
collision.translatesReferenceBoundsIntoBoundary = true
return collision
}()
lazy var fieldBehaviors: [UIFieldBehavior] =
{
var fieldBehaviors = [UIFieldBehavior]()
for _ in 0 ..< 2
{
let field = UIFieldBehavior.springField()
field.addItem(self.orangeView)
fieldBehaviors.append(field)
}
return fieldBehaviors
}()
lazy var itemBehavior: UIDynamicItemBehavior =
{
let itemBehavior = UIDynamicItemBehavior(items: [self.orangeView])
// Adjust these values to change the "stickiness" of the view
itemBehavior.density = 0.01
itemBehavior.resistance = 10
itemBehavior.friction = 0.0
itemBehavior.allowsRotation = false
return itemBehavior
}()
lazy var orangeView: UIView =
{
let widthHeight: CGFloat = 40.0
let orangeView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: widthHeight, height: widthHeight))
orangeView.backgroundColor = UIColor.orange
self.view.addSubview(orangeView)
return orangeView
}()
lazy var panGesture: UIPanGestureRecognizer =
{
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(sender:)))
return panGesture
}()
lazy var attachment: UIAttachmentBehavior =
{
let attachment = UIAttachmentBehavior(item: self.orangeView, attachedToAnchor: .zero)
return attachment
}()
override func viewDidLoad()
{
super.viewDidLoad()
dynamicAnimator.addBehavior(collision)
dynamicAnimator.addBehavior(itemBehavior)
for field in fieldBehaviors
{
dynamicAnimator.addBehavior(field)
}
orangeView.addGestureRecognizer(panGesture)
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
orangeView.center = view.center
dynamicAnimator.updateItem(usingCurrentState: orangeView)
for (index, field) in fieldBehaviors.enumerated()
{
field.position = CGPoint(x: view.bounds
.midX, y: view.bounds.height * (0.25 + 0.5 * CGFloat(index)))
field.region = UIRegion(size: CGSize(width: view.bounds.width, height: view.bounds.height * 0.5))
}
}
func handlePan(sender: UIPanGestureRecognizer)
{
let location = sender.location(in: view)
let velocity = sender.velocity(in: view)
switch sender.state
{
case .began:
attachment.anchorPoint = location
dynamicAnimator.addBehavior(attachment)
case .changed:
attachment.anchorPoint = location
case .cancelled, .ended, .failed, .possible:
itemBehavior.addLinearVelocity(velocity, for: self.orangeView)
dynamicAnimator.removeBehavior(attachment)
}
}
}
Objective-C
@interface ViewController ()
@property (nonatomic, strong) UIDynamicAnimator *dynamicAnimator;
@property (nonatomic, strong) UICollisionBehavior *collision;
@property (nonatomic, strong) UIAttachmentBehavior *attachment;
@property (nonatomic, strong) UIDynamicItemBehavior *itemBehavior;
@property (nonatomic, strong) NSArray <UIFieldBehavior *> *fieldBehaviors;
@property (nonatomic, strong) UIView *orangeView;
@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.dynamicAnimator addBehavior:self.collision];
[self.dynamicAnimator addBehavior:self.itemBehavior];
for (UIFieldBehavior *field in self.fieldBehaviors)
{
[self.dynamicAnimator addBehavior:field];
}
[self.orangeView addGestureRecognizer:self.panGesture];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.orangeView.center = self.view.center;
[self.dynamicAnimator updateItemUsingCurrentState:self.orangeView];
for (NSInteger i = 0; i < self.fieldBehaviors.count; i++)
{
UIFieldBehavior *field = self.fieldBehaviors[i];
field.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetHeight(self.view.bounds) * (0.25f + 0.5f * i));
field.region = [[UIRegion alloc]initWithSize:CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) * 0.5)];
}
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
CGPoint velocity = [sender velocityInView:self.view];
if (sender.state == UIGestureRecognizerStateBegan)
{
self.attachment.anchorPoint = location;
[self.dynamicAnimator addBehavior:self.attachment];
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
self.attachment.anchorPoint = location;
}
else if (sender.state == UIGestureRecognizerStateCancelled ||
sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStatePossible)
{
[self.itemBehavior addLinearVelocity:velocity forItem:self.orangeView];
[self.dynamicAnimator removeBehavior:self.attachment];
}
}
#pragma mark - Lazy Init
- (UIDynamicAnimator *)dynamicAnimator
{
if (!_dynamicAnimator)
{
_dynamicAnimator = [[UIDynamicAnimator alloc]initWithReferenceView:self.view];
}
return _dynamicAnimator;
}
- (UICollisionBehavior *)collision
{
if (!_collision)
{
_collision = [[UICollisionBehavior alloc]initWithItems:@[self.orangeView]];
_collision.translatesReferenceBoundsIntoBoundary = YES;
}
return _collision;
}
- (NSArray <UIFieldBehavior *> *)fieldBehaviors
{
if (!_fieldBehaviors)
{
NSMutableArray *fields = [[NSMutableArray alloc]init];
for (NSInteger i = 0; i < 2; i++)
{
UIFieldBehavior *field = [UIFieldBehavior springField];
[field addItem:self.orangeView];
[fields addObject:field];
}
_fieldBehaviors = fields;
}
return _fieldBehaviors;
}
- (UIDynamicItemBehavior *)itemBehavior
{
if (!_itemBehavior)
{
_itemBehavior = [[UIDynamicItemBehavior alloc]initWithItems:@[self.orangeView]];
// Adjust these values to change the "stickiness" of the view
_itemBehavior.density = 0.01;
_itemBehavior.resistance = 10;
_itemBehavior.friction = 0.0;
_itemBehavior.allowsRotation = NO;
}
return _itemBehavior;
}
- (UIView *)orangeView
{
if (!_orangeView)
{
CGFloat widthHeight = 40.0f;
_orangeView = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, widthHeight, widthHeight)];
_orangeView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:_orangeView];
}
return _orangeView;
}
- (UIPanGestureRecognizer *)panGesture
{
if (!_panGesture)
{
_panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePan:)];
}
return _panGesture;
}
- (UIAttachmentBehavior *)attachment
{
if (!_attachment)
{
_attachment = [[UIAttachmentBehavior alloc]initWithItem:self.orangeView attachedToAnchor:CGPointZero];
}
return _attachment;
}
@end
有关 UIFieldBehaviors
的更多信息,你可以看到 2015 WWDC 会议“UIKit 动态和视觉效果的新功能” 以及随附的示例代码 。