Skip to content

Commit

Permalink
improve anchor point handling during transforms, add example
Browse files Browse the repository at this point in the history
  • Loading branch information
u10int committed Feb 4, 2019
1 parent 437175f commit 924ffcf
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 7 deletions.
8 changes: 6 additions & 2 deletions Example/Kinetic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
2B61D64D1F2D4A7F009C9F91 /* PropertyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B61D64C1F2D4A7F009C9F91 /* PropertyTests.swift */; };
2B61D64F1F2D6837009C9F91 /* TweenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B61D64E1F2D6837009C9F91 /* TweenTests.swift */; };
2B61D6511F2D719D009C9F91 /* TimelineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B61D6501F2D719D009C9F91 /* TimelineTests.swift */; };
2B64C12522076DCC002B8119 /* AnchorPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B64C12422076DCC002B8119 /* AnchorPointViewController.swift */; };
2B92960F1E46B11900056AAC /* AdditiveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9296001E46B11900056AAC /* AdditiveViewController.swift */; };
2B9296101E46B11900056AAC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9296011E46B11900056AAC /* AppDelegate.swift */; };
2B9296111E46B11900056AAC /* BasicFromToTweenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B9296021E46B11900056AAC /* BasicFromToTweenViewController.swift */; };
Expand Down Expand Up @@ -57,6 +58,7 @@
2B61D64C1F2D4A7F009C9F91 /* PropertyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyTests.swift; sourceTree = "<group>"; };
2B61D64E1F2D6837009C9F91 /* TweenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweenTests.swift; sourceTree = "<group>"; };
2B61D6501F2D719D009C9F91 /* TimelineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineTests.swift; sourceTree = "<group>"; };
2B64C12422076DCC002B8119 /* AnchorPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchorPointViewController.swift; sourceTree = "<group>"; };
2B9296001E46B11900056AAC /* AdditiveViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdditiveViewController.swift; sourceTree = "<group>"; };
2B9296011E46B11900056AAC /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
2B9296021E46B11900056AAC /* BasicFromToTweenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicFromToTweenViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -156,6 +158,7 @@
2B9296011E46B11900056AAC /* AppDelegate.swift */,
2B92960E1E46B11900056AAC /* ViewController.swift */,
2B9296001E46B11900056AAC /* AdditiveViewController.swift */,
2B64C12422076DCC002B8119 /* AnchorPointViewController.swift */,
2B9296021E46B11900056AAC /* BasicFromToTweenViewController.swift */,
2B9296031E46B11900056AAC /* BasicFromTweenViewController.swift */,
2B9296041E46B11900056AAC /* BasicTweenViewController.swift */,
Expand Down Expand Up @@ -430,6 +433,7 @@
2B9296101E46B11900056AAC /* AppDelegate.swift in Sources */,
2B9296191E46B11900056AAC /* SequenceViewController.swift in Sources */,
2B5FD5DD1F2ECAD10085B250 /* PathTweenViewController.swift in Sources */,
2B64C12522076DCC002B8119 /* AnchorPointViewController.swift in Sources */,
2B92961A1E46B11900056AAC /* StaggerViewController.swift in Sources */,
2B92961C1E46B11900056AAC /* TransformViewController.swift in Sources */,
2B9296171E46B11900056AAC /* PhysicsViewController.swift in Sources */,
Expand Down Expand Up @@ -523,7 +527,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -570,7 +574,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
Expand Down
117 changes: 117 additions & 0 deletions Example/Kinetic/AnchorPointViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// AnchorPointViewController.swift
// Kinetic_Example
//
// Created by Nicholas Shipes on 2/3/19.
// Copyright © 2019 CocoaPods. All rights reserved.
//

import UIKit
import Kinetic

class AnchorPointViewController: ExampleViewController {
var square: UIView!
var squareContainer: UIView!

fileprivate let pickerView = UIPickerView()
fileprivate var pickerData: [String] = []
fileprivate var tween: Tween?

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

title = "Anchor Point"
pickerData = [
"Top-Left",
"Top",
"Top-Right",
"Left",
"Center",
"Right",
"Bottom-Left",
"Bottom",
"Bottom-Right"
]
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

pickerView.translatesAutoresizingMaskIntoConstraints = false
pickerView.dataSource = self
pickerView.delegate = self
view.addSubview(pickerView)

square = UIView()
square.translatesAutoresizingMaskIntoConstraints = false
square.backgroundColor = .red

/**
* `TransformContainerView` is a UIView subclass in Kinetic to be used when adjusting a view's anchorPoint.
*
* Need to place our animated view within a transparent container view since we will be adjusting the view's anchorPoint
* and applying a transform, which will prevent it from shifting the view's actual position within the view and allows
* us to still use AutoLayout for positioning the view
* re: https://stackoverflow.com/a/14105757
*/
squareContainer = TransformContainerView(view: square)
view.addSubview(squareContainer)

/*
* Since we're placing the animated square within a container view, we set position constraints on the container view
* and then width and height constraints on the actual square view, which will also size the container automatically
*/
NSLayoutConstraint.activate([squareContainer.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
squareContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
square.widthAnchor.constraint(equalToConstant: 100),
square.heightAnchor.constraint(equalToConstant: 100),
pickerView.topAnchor.constraint(equalTo: squareContainer.bottomAnchor, constant: 100),
pickerView.leftAnchor.constraint(equalTo: view.leftAnchor),
pickerView.rightAnchor.constraint(equalTo: view.rightAnchor),
pickerView.heightAnchor.constraint(equalToConstant: 250)])

let tween = square.tween().to(Rotation(Float.pi * 2)).duration(1.0).ease(Cubic.easeInOut)
animation = tween
self.tween = tween

pickerView.selectRow(4, inComponent: 0, animated: false)
}
}

extension AnchorPointViewController: UIPickerViewDelegate, UIPickerViewDataSource {

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var anchor = AnchorPoint.center

switch row {
case 0: anchor = .topLeft
case 1: anchor = .top
case 2: anchor = .topRight
case 3: anchor = .left
case 4: anchor = .center
case 5: anchor = .right
case 6: anchor = .bottomLeft
case 7: anchor = .bottom
case 8: anchor = .bottomRight
default: anchor = .center
}

tween?.anchor(anchor)
}
}
2 changes: 2 additions & 0 deletions Example/Kinetic/ExampleViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class ExampleViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .white

playButton = UIButton(type: .roundedRect)
playButton.translatesAutoresizingMaskIntoConstraints = false
Expand Down
1 change: 1 addition & 0 deletions Example/Kinetic/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ViewController: UIViewController {
rows.append(GroupTweenViewController())
rows.append(SequenceViewController())
rows.append(TransformViewController())
rows.append(AnchorPointViewController())
rows.append(AdditiveViewController())
rows.append(PhysicsViewController())
rows.append(StaggerViewController())
Expand Down
2 changes: 1 addition & 1 deletion Kinetic.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Pod::Spec.new do |s|

s.name = "Kinetic"
s.version = "1.0.1"
s.version = "1.1.0"
s.summary = "A super-flexible tweening library for iOS in Swift inspired by GSAP."

s.description = <<-DESC
Expand Down
1 change: 1 addition & 0 deletions Pod/Classes/Animator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ final public class Animator: Equatable {
fileprivate (set) public var duration: Double
fileprivate (set) public var key: String
public var timingFunction: TimingFunction = Linear().timingFunction
public var anchorPoint: CGPoint = CGPoint(x: 0.5, y: 0.5)
public var additive: Bool = true
public var changed: ((Animator, Property) -> Void)?
public var finished: Bool {
Expand Down
12 changes: 8 additions & 4 deletions Pod/Classes/Tween.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ public class Tween: Animation {
}
fileprivate var propertiesByType: Dictionary<String, FromToValue> = [String: FromToValue]()
private(set) var animators = [String: Animator]()

fileprivate var needsPropertyPrep = false
fileprivate var anchorPoint: CGPoint = AnchorPoint.center.point()

public var additive = true;

Expand All @@ -88,6 +88,11 @@ public class Tween: Animation {
self.target = target
super.init()

self.on(.started) { [unowned self] (animation) in
guard var view = self.target as? UIView else { return }
view.anchorPoint = self.anchorPoint
}

TweenCache.session.cache(self, target: target)
}

Expand Down Expand Up @@ -145,9 +150,7 @@ public class Tween: Animation {

@discardableResult
open func anchorPoint(_ point: CGPoint) -> Tween {
if var view = target as? ViewType {
view.anchorPoint = point
}
anchorPoint = point
return self
}

Expand Down Expand Up @@ -371,6 +374,7 @@ public class Tween: Animation {
let animator = Animator(from: transformFrom, to: transformTo, duration: duration, timingFunction: timingFunction)
animator.spring = spring
animator.additive = false
animator.anchorPoint = anchorPoint
animator.onChange({ [weak self] (animator, value) in
self?.target.apply(value)
})
Expand Down
37 changes: 37 additions & 0 deletions Pod/Classes/Tweenable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,23 @@ extension ViewType where Self: UIView {
return layer.anchorPoint
}
set {
// adjust the layer's anchorPoint without moving the view
// re: https://www.hackingwithswift.com/example-code/calayer/how-to-change-a-views-anchor-point-without-moving-it
var newPoint = CGPoint(x: bounds.size.width * newValue.x, y: bounds.size.height * newValue.y)
var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y);

newPoint = newPoint.applying(transform)
oldPoint = oldPoint.applying(transform)

var position = layer.position

position.x -= oldPoint.x
position.x += newPoint.x

position.y -= oldPoint.y
position.y += newPoint.y

layer.position = position
layer.anchorPoint = newValue
}
}
Expand Down Expand Up @@ -325,3 +342,23 @@ extension ViewType where Self: CALayer {
}
}
}

public class TransformContainerView: UIView {

public init(view: UIView) {
super.init(frame: .zero)

self.translatesAutoresizingMaskIntoConstraints = false
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)

NSLayoutConstraint.activate([view.topAnchor.constraint(equalTo: self.topAnchor),
self.bottomAnchor.constraint(equalTo: view.bottomAnchor),
self.leftAnchor.constraint(equalTo: view.leftAnchor),
self.rightAnchor.constraint(equalTo: view.rightAnchor)])
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

0 comments on commit 924ffcf

Please sign in to comment.