將動態動畫位置更改對映到邊界
此示例顯示如何自定義 UIDynamicItem
協議以對映動態動畫檢視的位置更改以限制更改以建立以彈性方式擴充套件和收縮的 UIButton
。
https://i.stack.imgur.com/9R0zL.gif
首先,我們需要建立一個實現 UIDynamicItem
的新協議,但它也具有可設定且可獲取的 bounds
屬性。
迅速
protocol ResizableDynamicItem: UIDynamicItem
{
var bounds: CGRect { set get }
}
extension UIView: ResizableDynamicItem {}
Objective-C
@protocol ResizableDynamicItem <UIDynamicItem>
@property (nonatomic, readwrite) CGRect bounds;
@end
然後我們將建立一個包裝器物件,它將包裹 UIDynamicItem
,但會將中心更改對映到專案的寬度和高度。我們還將為基礎專案的 bounds
和 transform
提供 passthrough。這將導致動態動畫製作者對中心的任何更改 x 和底層專案的 y 值將應用於專案的寬度和高度。
迅速
final class PositionToBoundsMapping: NSObject, UIDynamicItem
{
var target: ResizableDynamicItem
init(target: ResizableDynamicItem)
{
self.target = target
super.init()
}
var bounds: CGRect
{
get
{
return self.target.bounds
}
}
var center: CGPoint
{
get
{
return CGPoint(x: self.target.bounds.width, y: self.target.bounds.height)
}
set
{
self.target.bounds = CGRect(x: 0.0, y: 0.0, width: newValue.x, height: newValue.y)
}
}
var transform: CGAffineTransform
{
get
{
return self.target.transform
}
set
{
self.target.transform = newValue
}
}
}
Objective-C
@interface PositionToBoundsMapping ()
@property (nonatomic, strong) id<ResizableDynamicItem> target;
@end
@implementation PositionToBoundsMapping
- (instancetype)initWithTarget:(id<ResizableDynamicItem>)target
{
self = [super init];
if (self)
{
_target = target;
}
return self;
}
- (CGRect)bounds
{
return self.target.bounds;
}
- (CGPoint)center
{
return CGPointMake(self.target.bounds.size.width, self.target.bounds.size.height);
}
- (void)setCenter:(CGPoint)center
{
self.target.bounds = CGRectMake(0, 0, center.x, center.y);
}
- (CGAffineTransform)transform
{
return self.target.transform;
}
- (void)setTransform:(CGAffineTransform)transform
{
self.target.transform = transform;
}
@end
最後,我們將建立一個有一個按鈕的 UIViewController
。按下按鈕後,我們將建立 PositionToBoundsMapping
,按鈕作為包裝的動態專案。我們建立一個 UIAttachmentBehavior
到它的當前位置然後新增一個瞬時 UIPushBehavior
到它。但是因為我們已經對映了更改其邊界,所以按鈕不會移動,而是會增長和縮小。
迅速
final class ViewController: UIViewController
{
lazy var button: UIButton =
{
let button = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 200.0))
button.backgroundColor = .red
button.layer.cornerRadius = 15.0
button.setTitle("Tap Me", for: .normal)
self.view.addSubview(button)
return button
}()
var buttonBounds = CGRect.zero
var animator: UIDynamicAnimator?
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .white
button.addTarget(self, action: #selector(self.didPressButton(sender:)), for: .touchUpInside)
buttonBounds = button.bounds
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
button.center = view.center
}
func didPressButton(sender: UIButton)
{
// Reset bounds so if button is press twice in a row, previous changes don't propogate
button.bounds = buttonBounds
let animator = UIDynamicAnimator(referenceView: view)
// Create mapping
let buttonBoundsDynamicItem = PositionToBoundsMapping(target: button)
// Add Attachment behavior
let attachmentBehavior = UIAttachmentBehavior(item: buttonBoundsDynamicItem, attachedToAnchor: buttonBoundsDynamicItem.center)
// Higher frequency faster oscillation
attachmentBehavior.frequency = 2.0
// Lower damping longer oscillation lasts
attachmentBehavior.damping = 0.1
animator.addBehavior(attachmentBehavior)
let pushBehavior = UIPushBehavior(items: [buttonBoundsDynamicItem], mode: .instantaneous)
// Change angle to determine how much height/ width should change 45° means heigh:width is 1:1
pushBehavior.angle = .pi / 4.0
// Larger magnitude means bigger change
pushBehavior.magnitude = 30.0
animator.addBehavior(pushBehavior)
pushBehavior.active = true
// Hold refrence so animator is not released
self.animator = animator
}
}
Objective-C
@interface ViewController ()
@property (nonatomic, strong) UIButton *button;
@property (nonatomic, assign) CGRect buttonBounds;
@property (nonatomic, strong) UIDynamicAnimator *animator;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
self.buttonBounds = self.button.bounds;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.button.center = self.view.center;
}
- (UIButton *)button
{
if (!_button)
{
_button = [[UIButton alloc]initWithFrame:CGRectMake(0.0, 0.0, 200.0, 200.0)];
_button.backgroundColor = [UIColor redColor];
_button.layer.cornerRadius = 15.0;
[_button setTitle:@"Tap Me" forState:UIControlStateNormal];
[self.view addSubview:_button];
}
return _button;
}
- (void)didTapButton:(id)sender
{
self.button.bounds = self.buttonBounds;
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
PositionToBoundsMapping *buttonBoundsDynamicItem = [[PositionToBoundsMapping alloc]initWithTarget:sender];
UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc]initWithItem:buttonBoundsDynamicItem attachedToAnchor:buttonBoundsDynamicItem.center];
[attachmentBehavior setFrequency:2.0];
[attachmentBehavior setDamping:0.3];
[animator addBehavior:attachmentBehavior];
UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[buttonBoundsDynamicItem] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.angle = M_PI_4;
pushBehavior.magnitude = 2.0;
[animator addBehavior:pushBehavior];
[pushBehavior setActive:TRUE];
self.animator = animator;
}
@end
有關更多資訊,請參閱 UIKit Dynamics Catalog