StickyCorners/StickyCornersViewController.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
StickyCornersViewController is a UIViewController subclass demonstrating the use of UIFieldBehavior. |
*/ |
import UIKit |
class StickyCornersViewController: UIViewController { |
// MARK: Properties |
// Dynamics. |
var animator: UIDynamicAnimator! |
var stickyBehavior: StickyCornersBehavior! |
var itemView: UIView! |
// Touch handling. |
var offset = CGPoint.zero |
let itemAspectRatio: CGFloat = 0.70 |
// MARK: UIViewController |
override func viewDidLoad() { |
super.viewDidLoad() |
// Determine a reasonable item size. |
let screenBounds = UIScreen.main.bounds |
let length = floor(0.1 * max(screenBounds.width, screenBounds.height)) |
/* |
Create the itemView, add a pan gesture recognizer, then add the `itemView` |
as a subview of the viewController's view. |
*/ |
itemView = UIView(frame: CGRect(x: 0, y: 0, width: length, height: floor(length / itemAspectRatio))) |
itemView.autoresizingMask = [] |
itemView.backgroundColor = UIColor.red |
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(StickyCornersViewController.pan(_:))) |
itemView.addGestureRecognizer(panGestureRecognizer) |
view.addSubview(itemView) |
// Create a UIDynamicAnimator. |
animator = UIDynamicAnimator(referenceView: view) |
/* |
Create a StickyCornersBehavior with the itemView and a corner inset, |
then add it to the animator. |
*/ |
stickyBehavior = StickyCornersBehavior(item: itemView, cornerInset: length * 0.5) |
animator.addBehavior(stickyBehavior) |
} |
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { |
super.viewWillTransition(to: size, with: coordinator) |
// Ensure the item stays on screen during a bounds change. |
guard let corner = stickyBehavior.currentCorner else { return } |
stickyBehavior.isEnabled = false |
let bounds = CGRect(origin: CGPoint.zero, size: size) |
stickyBehavior.updateFieldsInBounds(bounds) |
coordinator.animate(alongsideTransition: { context in |
self.itemView.center = self.stickyBehavior.positionForCorner(corner) |
}, |
completion: { context in |
self.stickyBehavior.isEnabled = true |
}) |
} |
// MARK: Gesture Callbacks |
func pan(_ pan: UIPanGestureRecognizer) { |
var location = pan.location(in: view) |
switch pan.state { |
case .began: |
// Capture the initial touch offset from the itemView's center. |
let center = itemView.center |
offset.x = location.x - center.x |
offset.y = location.y - center.y |
// Disable the behavior while the item is manipulated by the pan recognizer. |
stickyBehavior.isEnabled = false |
case .changed: |
// Get reference bounds. |
let referenceBounds = view.bounds |
let referenceWidth = referenceBounds.width |
let referenceHeight = referenceBounds.height |
// Get item bounds. |
let itemBounds = itemView.bounds |
let itemHalfWidth = itemBounds.width / 2.0 |
let itemHalfHeight = itemBounds.height / 2.0 |
// Apply the initial offset. |
location.x -= offset.x |
location.y -= offset.y |
// Bound the item position inside the reference view. |
location.x = max(itemHalfWidth, location.x) |
location.x = min(referenceWidth - itemHalfWidth, location.x) |
location.y = max(itemHalfHeight, location.y) |
location.y = min(referenceHeight - itemHalfHeight, location.y) |
// Apply the resulting item center. |
itemView.center = location |
case .cancelled, .ended: |
// Get the current velocity of the item from the pan gesture recognizer. |
let velocity = pan.velocity(in: view) |
// Re-enable the stickyCornersBehavior. |
stickyBehavior.isEnabled = true |
// Add the current velocity to the sticky corners behavior. |
stickyBehavior.addLinearVelocity(velocity) |
default: () |
} |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-14