mirror of
https://github.com/exyte/Macaw.git
synced 2024-09-11 05:05:23 +03:00
Merge pull request #671 from f3dm76/task/layer-animation-example
Add correct zoom-scroll handling
This commit is contained in:
commit
15027f0e53
@ -13,6 +13,9 @@ class AnimationsHierarchyViewController: UIViewController {
|
||||
|
||||
@IBOutlet weak var animView: MacawView!
|
||||
|
||||
var startCallbacks: [()->()] = []
|
||||
var stopCallbacks: [()->()] = []
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
@ -20,6 +23,22 @@ class AnimationsHierarchyViewController: UIViewController {
|
||||
animView.zoom.enable()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
startCallbacks.forEach {
|
||||
$0()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
stopCallbacks.forEach {
|
||||
$0()
|
||||
}
|
||||
}
|
||||
|
||||
func createTree(height: Int) -> Node {
|
||||
let rect = Rect(w: 10, h: 10)
|
||||
|
||||
@ -52,7 +71,14 @@ class AnimationsHierarchyViewController: UIViewController {
|
||||
func createLeaf(childForm: Locus, xDelta: Double, yDelta: Double) -> Group {
|
||||
let inset = childForm.bounds().w / 2
|
||||
let leaf = Shape(form: childForm, fill: Color.teal, place: .move(dx: -inset, dy: -inset))
|
||||
leaf.placeVar.animation(angle: 2 * .pi, during: 5).cycle().play()
|
||||
|
||||
let animation = leaf.placeVar.animation(angle: 2 * .pi, during: 5).cycle()
|
||||
startCallbacks.append({
|
||||
animation.play()
|
||||
})
|
||||
stopCallbacks.append({
|
||||
animation.stop()
|
||||
})
|
||||
|
||||
let leafGroup = [leaf].group(place: .move(dx: xDelta, dy: yDelta))
|
||||
leaf.onTap { _ in
|
||||
|
@ -24,11 +24,8 @@ class BasicAnimation: Animation {
|
||||
|
||||
weak var node: Node? {
|
||||
didSet {
|
||||
if !(self is CombineAnimation || self is AnimationSequence || self is EmptyAnimation) {
|
||||
node?.animations.append(self)
|
||||
if let group = node as? Group {
|
||||
for node in group.contents {
|
||||
node.animations.append(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +101,8 @@ class BasicAnimation: Animation {
|
||||
}
|
||||
|
||||
removeFunc?()
|
||||
node?.animations.removeAll { $0 === self }
|
||||
nodeRenderer?.freeLayer()
|
||||
}
|
||||
|
||||
override open func pause() {
|
||||
@ -115,6 +114,8 @@ class BasicAnimation: Animation {
|
||||
}
|
||||
|
||||
removeFunc?()
|
||||
node?.animations.removeAll { $0 === self }
|
||||
nodeRenderer?.freeLayer()
|
||||
}
|
||||
|
||||
override func state() -> AnimationState {
|
||||
|
@ -143,9 +143,9 @@ class AnimationProducer {
|
||||
}
|
||||
|
||||
// MARK: - Sequence animation
|
||||
func addAnimationSequence(_ animationSequnce: Animation,
|
||||
func addAnimationSequence(_ animationSequence: Animation,
|
||||
_ context: AnimationContext) {
|
||||
guard let sequence = animationSequnce as? AnimationSequence else {
|
||||
guard let sequence = animationSequence as? AnimationSequence else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -198,7 +198,7 @@ class AnimationProducer {
|
||||
}
|
||||
|
||||
// MARK: - Stored animation
|
||||
func addStoredAnimations(_ node: Node, _ view: MacawView) {
|
||||
func addStoredAnimations(_ node: Node, _ view: DrawingView) {
|
||||
addStoredAnimations(node, AnimationContext())
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import AppKit
|
||||
|
||||
class AnimationUtils {
|
||||
|
||||
class func layerForNodeRenderer(_ renderer: NodeRenderer, _ context: AnimationContext, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer {
|
||||
class func layerForNodeRenderer(_ renderer: NodeRenderer, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer {
|
||||
|
||||
let node = renderer.node
|
||||
if let cachedLayer = renderer.layer {
|
||||
|
@ -115,8 +115,8 @@ extension AnimationProducer {
|
||||
// MARK: - Combine animation
|
||||
func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) {
|
||||
guard let combine = combineAnimation as? CombineAnimation,
|
||||
let renderer = combine.nodeRenderer,
|
||||
let _ = renderer.view else {
|
||||
let _ = combine.nodeRenderer,
|
||||
let node = combine.node else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -176,6 +176,7 @@ extension AnimationProducer {
|
||||
}
|
||||
|
||||
combine.removeFunc = {
|
||||
node.animations.removeAll { $0 === combine }
|
||||
animations.forEach { animation in
|
||||
animation.removeFunc?()
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte
|
||||
let toLocus = morphingAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
|
||||
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: false)
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
|
||||
|
||||
// Creating proper animation
|
||||
let generatedAnimation = pathAnimation(
|
||||
@ -94,6 +94,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte
|
||||
let animationId = animation.ID
|
||||
layer.add(generatedAnimation, forKey: animationId)
|
||||
animation.removeFunc = { [weak layer] in
|
||||
shape.animations.removeAll { $0 === animation }
|
||||
layer?.removeAnimation(forKey: animationId)
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,15 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex
|
||||
node.opacityVar.value = opacityAnimation.getVFunc()(1.0)
|
||||
}
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
|
||||
renderer.layer?.animationLayer.opacity = Float(node.opacity)
|
||||
CATransaction.commit()
|
||||
|
||||
if !animation.paused {
|
||||
animation.removeFunc?()
|
||||
}
|
||||
|
||||
renderer.freeLayer()
|
||||
|
||||
if !animation.cycled &&
|
||||
@ -58,10 +67,11 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex
|
||||
completion()
|
||||
}
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation)
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation)
|
||||
let animationId = animation.ID
|
||||
layer.add(generatedAnimation, forKey: animationId)
|
||||
animation.removeFunc = { [weak layer] in
|
||||
node.animations.removeAll { $0 === animation }
|
||||
layer?.removeAnimation(forKey: animationId)
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
|
||||
let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
|
||||
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: false)
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
|
||||
|
||||
// Creating proper animation
|
||||
let generatedAnimation = generateShapeAnimation(context,
|
||||
|
@ -22,7 +22,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
|
||||
let transactionsDisabled = CATransaction.disableActions()
|
||||
CATransaction.setDisableActions(true)
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation, shouldRenderContent: true)
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: true)
|
||||
|
||||
// Creating proper animation
|
||||
let generatedAnimation = transformAnimationByFunc(transformAnimation,
|
||||
@ -54,6 +54,15 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
|
||||
node.placeVar.value = transformAnimation.getVFunc()(1.0)
|
||||
}
|
||||
|
||||
CATransaction.begin()
|
||||
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
|
||||
renderer.layer?.animationLayer.transform = CATransform3DMakeAffineTransform(node.place.toCG())
|
||||
CATransaction.commit()
|
||||
|
||||
if !animation.paused {
|
||||
animation.removeFunc?()
|
||||
}
|
||||
|
||||
renderer.freeLayer()
|
||||
|
||||
if !animation.cycled &&
|
||||
@ -68,6 +77,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
|
||||
let animationId = animation.ID
|
||||
layer.add(generatedAnimation, forKey: animationId)
|
||||
animation.removeFunc = { [weak layer] in
|
||||
node.animations.removeAll { $0 === animation }
|
||||
layer?.removeAnimation(forKey: animationId)
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ public extension MacawView {
|
||||
y: -size.height / bounds.height
|
||||
)
|
||||
|
||||
context.cgContext = ctx
|
||||
renderer?.render(in: ctx, force: false, opacity: node.opacity)
|
||||
drawingView.context.cgContext = ctx
|
||||
drawingView.renderer?.render(in: ctx, force: false, opacity: node.opacity)
|
||||
|
||||
ctx.endPDFPage()
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class GroupRenderer: NodeRenderer {
|
||||
return group
|
||||
}
|
||||
|
||||
init(group: Group, view: MacawView?, parentRenderer: GroupRenderer? = nil) {
|
||||
init(group: Group, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
|
||||
self.group = group
|
||||
super.init(node: group, view: view, parentRenderer: parentRenderer)
|
||||
updateRenderers()
|
||||
|
@ -17,7 +17,7 @@ class ImageRenderer: NodeRenderer {
|
||||
return image
|
||||
}
|
||||
|
||||
init(image: Image, view: MacawView?, parentRenderer: GroupRenderer? = nil) {
|
||||
init(image: Image, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
|
||||
self.image = image
|
||||
super.init(node: image, view: view, parentRenderer: parentRenderer)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class CachedLayer {
|
||||
|
||||
class NodeRenderer {
|
||||
|
||||
weak var view: MacawView?
|
||||
weak var view: DrawingView?
|
||||
var sceneLayer: CALayer? {
|
||||
return view?.mLayer
|
||||
}
|
||||
@ -71,7 +71,7 @@ class NodeRenderer {
|
||||
fatalError("Unsupported")
|
||||
}
|
||||
|
||||
init(node: Node, view: MacawView?, parentRenderer: GroupRenderer? = nil) {
|
||||
init(node: Node, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
|
||||
self.view = view
|
||||
self.parentRenderer = parentRenderer
|
||||
|
||||
|
@ -5,10 +5,10 @@ import UIKit
|
||||
#endif
|
||||
|
||||
class RenderContext {
|
||||
weak var view: MacawView?
|
||||
weak var view: DrawingView?
|
||||
var cgContext: CGContext?
|
||||
|
||||
init(view: MacawView?) {
|
||||
init(view: DrawingView?) {
|
||||
self.view = view
|
||||
self.cgContext = nil
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class RenderUtils {
|
||||
return p
|
||||
}
|
||||
|
||||
class func createNodeRenderer(_ node: Node, view: MacawView?, parentRenderer: GroupRenderer? = nil) -> NodeRenderer {
|
||||
class func createNodeRenderer(_ node: Node, view: DrawingView?, parentRenderer: GroupRenderer? = nil) -> NodeRenderer {
|
||||
if let group = node as? Group {
|
||||
return GroupRenderer(group: group, view: view, parentRenderer: parentRenderer)
|
||||
} else if let shape = node as? Shape {
|
||||
|
@ -10,7 +10,7 @@ class ShapeRenderer: NodeRenderer {
|
||||
|
||||
var shape: Shape
|
||||
|
||||
init(shape: Shape, view: MacawView?, parentRenderer: GroupRenderer? = nil) {
|
||||
init(shape: Shape, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
|
||||
self.shape = shape
|
||||
super.init(node: shape, view: view, parentRenderer: parentRenderer)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class TextRenderer: NodeRenderer {
|
||||
return text
|
||||
}
|
||||
|
||||
init(text: Text, view: MacawView?, parentRenderer: GroupRenderer? = nil) {
|
||||
init(text: Text, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
|
||||
self.text = text
|
||||
super.init(node: text, view: view, parentRenderer: parentRenderer)
|
||||
}
|
||||
|
@ -10,8 +10,196 @@ import AppKit
|
||||
/// MacawView is a main class used to embed Macaw scene into your Cocoa UI.
|
||||
/// You could create your own view extended from MacawView with predefined scene.
|
||||
///
|
||||
|
||||
open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
|
||||
internal var drawingView = DrawingView()
|
||||
|
||||
public let zoom = MacawZoom()
|
||||
|
||||
open var node: Node {
|
||||
get { return drawingView.node }
|
||||
set { drawingView.node = newValue }
|
||||
}
|
||||
|
||||
open var contentLayout: ContentLayout {
|
||||
get { return drawingView.contentLayout }
|
||||
set { drawingView.contentLayout = newValue }
|
||||
}
|
||||
|
||||
open override var contentMode: MViewContentMode {
|
||||
get { return drawingView.contentMode }
|
||||
set { drawingView.contentMode = newValue }
|
||||
}
|
||||
|
||||
open var place: Transform {
|
||||
get { return drawingView.place }
|
||||
}
|
||||
|
||||
open var placeVar: Variable<Transform> {
|
||||
get { return drawingView.placeVar }
|
||||
}
|
||||
|
||||
override open var frame: CGRect {
|
||||
didSet {
|
||||
super.frame = frame
|
||||
drawingView.frame = frame
|
||||
}
|
||||
}
|
||||
|
||||
override open var intrinsicContentSize: CGSize {
|
||||
get { return drawingView.intrinsicContentSize }
|
||||
}
|
||||
|
||||
internal var renderer: NodeRenderer? {
|
||||
get { return drawingView.renderer }
|
||||
set { drawingView.renderer = newValue }
|
||||
}
|
||||
|
||||
#if os(OSX)
|
||||
open override var layer: CALayer? {
|
||||
didSet {
|
||||
guard self.layer != nil else {
|
||||
return
|
||||
}
|
||||
initializeView()
|
||||
|
||||
renderer = RenderUtils.createNodeRenderer(node, view: drawingView)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@objc public convenience required init?(coder aDecoder: NSCoder) {
|
||||
self.init(node: Group(), coder: aDecoder)
|
||||
}
|
||||
|
||||
@objc public init?(node: Node, coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
self.node = node
|
||||
self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView)
|
||||
|
||||
zoom.initialize(view: self, onChange: onZoomChange)
|
||||
initializeView()
|
||||
}
|
||||
|
||||
public convenience init(node: Node, frame: CGRect) {
|
||||
self.init(frame: frame)
|
||||
|
||||
self.node = node
|
||||
self.renderer = RenderUtils.createNodeRenderer(node, view: drawingView)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
zoom.initialize(view: self, onChange: onZoomChange)
|
||||
initializeView()
|
||||
}
|
||||
|
||||
private func onZoomChange(t: Transform) {
|
||||
if let viewLayer = drawingView.mLayer {
|
||||
viewLayer.transform = CATransform3DMakeAffineTransform(t.toCG())
|
||||
}
|
||||
}
|
||||
|
||||
func initializeView() {
|
||||
|
||||
if !self.subviews.contains(drawingView) {
|
||||
if self.backgroundColor == nil {
|
||||
self.backgroundColor = .white
|
||||
}
|
||||
self.addSubview(drawingView)
|
||||
drawingView.backgroundColor = .clear
|
||||
drawingView.initializeView()
|
||||
|
||||
drawingView.translatesAutoresizingMaskIntoConstraints = false
|
||||
drawingView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
|
||||
drawingView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
|
||||
drawingView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
|
||||
drawingView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
|
||||
|
||||
#if os(iOS)
|
||||
self.clipsToBounds = true
|
||||
drawingView.isUserInteractionEnabled = false
|
||||
#endif
|
||||
}
|
||||
|
||||
let tapRecognizer = MTapGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleTap(recognizer:)))
|
||||
let longTapRecognizer = MLongPressGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleLongTap(recognizer:)))
|
||||
let panRecognizer = MPanGestureRecognizer(target: drawingView, action: #selector(DrawingView.handlePan))
|
||||
let rotationRecognizer = MRotationGestureRecognizer(target: drawingView, action: #selector(DrawingView.handleRotation))
|
||||
let pinchRecognizer = MPinchGestureRecognizer(target: drawingView, action: #selector(DrawingView.handlePinch))
|
||||
|
||||
tapRecognizer.delegate = self
|
||||
longTapRecognizer.delegate = self
|
||||
panRecognizer.delegate = self
|
||||
rotationRecognizer.delegate = self
|
||||
pinchRecognizer.delegate = self
|
||||
|
||||
tapRecognizer.cancelsTouchesInView = false
|
||||
longTapRecognizer.cancelsTouchesInView = false
|
||||
panRecognizer.cancelsTouchesInView = false
|
||||
rotationRecognizer.cancelsTouchesInView = false
|
||||
pinchRecognizer.cancelsTouchesInView = false
|
||||
|
||||
self.removeGestureRecognizers()
|
||||
self.addGestureRecognizer(tapRecognizer)
|
||||
self.addGestureRecognizer(longTapRecognizer)
|
||||
self.addGestureRecognizer(panRecognizer)
|
||||
self.addGestureRecognizer(rotationRecognizer)
|
||||
self.addGestureRecognizer(pinchRecognizer)
|
||||
}
|
||||
|
||||
open override func mTouchesBegan(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
super.mTouchesBegan(touches, with: event)
|
||||
zoom.touchesBegan(touches)
|
||||
|
||||
drawingView.touchesBegan(touchPoints: convert(touches: touches))
|
||||
}
|
||||
|
||||
open override func mTouchesMoved(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
super.mTouchesMoved(touches, with: event)
|
||||
zoom.touchesMoved(touches)
|
||||
|
||||
drawingView.touchesMoved(touchPoints: convert(touches: touches))
|
||||
}
|
||||
|
||||
open override func mTouchesEnded(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
super.mTouchesEnded(touches, with: event)
|
||||
zoom.touchesEnded(touches)
|
||||
|
||||
drawingView.touchesEnded(touchPoints: convert(touches: touches))
|
||||
}
|
||||
|
||||
override open func mTouchesCancelled(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
super.mTouchesCancelled(touches, with: event)
|
||||
zoom.touchesEnded(touches)
|
||||
|
||||
drawingView.touchesEnded(touchPoints: convert(touches: touches))
|
||||
}
|
||||
|
||||
private func convert(touches: Set<MTouch>) -> [MTouchEvent] {
|
||||
return touches.map { touch -> MTouchEvent in
|
||||
let location = touch.location(in: self).toMacaw()
|
||||
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
|
||||
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MGestureRecognizerDelegate
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldReceive touch: MTouch) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: MGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
internal class DrawingView: MView {
|
||||
|
||||
/// Scene root node
|
||||
open var node: Node = Group() {
|
||||
didSet {
|
||||
@ -42,8 +230,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public let zoom = MacawZoom()
|
||||
|
||||
public var place: Transform {
|
||||
return placeManager.placeVar.value
|
||||
}
|
||||
@ -52,8 +238,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
return placeManager.placeVar
|
||||
}
|
||||
|
||||
private let placeManager = RootPlaceManager()
|
||||
|
||||
override open var frame: CGRect {
|
||||
didSet {
|
||||
super.frame = frame
|
||||
@ -68,6 +252,14 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override open var intrinsicContentSize: CGSize {
|
||||
if let bounds = node.bounds {
|
||||
return bounds.size().toCG()
|
||||
} else {
|
||||
return CGSize(width: MNoIntrinsicMetric(), height: MNoIntrinsicMetric())
|
||||
}
|
||||
}
|
||||
|
||||
override open func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
|
||||
@ -78,14 +270,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
animationProducer.addStoredAnimations(node, self)
|
||||
}
|
||||
|
||||
override open var intrinsicContentSize: CGSize {
|
||||
if let bounds = node.bounds {
|
||||
return bounds.size().toCG()
|
||||
} else {
|
||||
return CGSize(width: MNoIntrinsicMetric(), height: MNoIntrinsicMetric())
|
||||
}
|
||||
}
|
||||
|
||||
private let placeManager = RootPlaceManager()
|
||||
private let layoutHelper = LayoutHelper()
|
||||
|
||||
var touchesMap = [MTouchEvent: [NodePath]]()
|
||||
@ -98,82 +283,17 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
var toRender = true
|
||||
var frameSetFirstTime = false
|
||||
|
||||
#if os(OSX)
|
||||
open override var layer: CALayer? {
|
||||
didSet {
|
||||
guard self.layer != nil else {
|
||||
return
|
||||
func initializeView() {
|
||||
self.contentLayout = .none
|
||||
self.context = RenderContext(view: self)
|
||||
}
|
||||
initializeView()
|
||||
|
||||
self.renderer = RenderUtils.createNodeRenderer(node, view: self)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@objc public init?(node: Node, coder aDecoder: NSCoder) {
|
||||
@objc public required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
zoom.initialize(view: self, onChange: onZoomChange)
|
||||
|
||||
initializeView()
|
||||
|
||||
self.node = node
|
||||
self.renderer = RenderUtils.createNodeRenderer(node, view: self)
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
public convenience init(node: Node, frame: CGRect) {
|
||||
self.init(frame: frame)
|
||||
|
||||
self.node = node
|
||||
self.renderer = RenderUtils.createNodeRenderer(node, view: self)
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
zoom.initialize(view: self, onChange: onZoomChange)
|
||||
|
||||
initializeView()
|
||||
}
|
||||
|
||||
@objc public convenience required init?(coder aDecoder: NSCoder) {
|
||||
self.init(node: Group(), coder: aDecoder)
|
||||
}
|
||||
|
||||
private func onZoomChange(t: Transform) {
|
||||
placeManager.setZoom(place: t)
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
|
||||
func initializeView() {
|
||||
self.contentLayout = .none
|
||||
self.context = RenderContext(view: self)
|
||||
|
||||
let tapRecognizer = MTapGestureRecognizer(target: self, action: #selector(MacawView.handleTap))
|
||||
let longTapRecognizer = MLongPressGestureRecognizer(target: self, action: #selector(MacawView.handleLongTap(recognizer:)))
|
||||
let panRecognizer = MPanGestureRecognizer(target: self, action: #selector(MacawView.handlePan))
|
||||
let rotationRecognizer = MRotationGestureRecognizer(target: self, action: #selector(MacawView.handleRotation))
|
||||
let pinchRecognizer = MPinchGestureRecognizer(target: self, action: #selector(MacawView.handlePinch))
|
||||
|
||||
tapRecognizer.delegate = self
|
||||
longTapRecognizer.delegate = self
|
||||
panRecognizer.delegate = self
|
||||
rotationRecognizer.delegate = self
|
||||
pinchRecognizer.delegate = self
|
||||
|
||||
tapRecognizer.cancelsTouchesInView = false
|
||||
longTapRecognizer.cancelsTouchesInView = false
|
||||
panRecognizer.cancelsTouchesInView = false
|
||||
rotationRecognizer.cancelsTouchesInView = false
|
||||
pinchRecognizer.cancelsTouchesInView = false
|
||||
|
||||
self.removeGestureRecognizers()
|
||||
self.addGestureRecognizer(tapRecognizer)
|
||||
self.addGestureRecognizer(longTapRecognizer)
|
||||
self.addGestureRecognizer(panRecognizer)
|
||||
self.addGestureRecognizer(rotationRecognizer)
|
||||
self.addGestureRecognizer(pinchRecognizer)
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
@ -194,12 +314,12 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
guard let renderer = renderer else {
|
||||
return
|
||||
}
|
||||
renderer.calculateZPositionRecursively()
|
||||
|
||||
// TODO: actually we should track all changes
|
||||
placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw()))
|
||||
|
||||
ctx.concatenate(self.place.toCG())
|
||||
|
||||
renderer.calculateZPositionRecursively()
|
||||
renderer.render(in: ctx, force: false, opacity: node.opacity)
|
||||
}
|
||||
|
||||
@ -229,10 +349,8 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
// MARK: - Touches
|
||||
override func mTouchesBegan(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
zoom.touchesBegan(touches)
|
||||
func touchesBegan(touchPoints: [MTouchEvent]) {
|
||||
|
||||
let touchPoints = convert(touches: touches)
|
||||
if !self.node.shouldCheckForPressed() &&
|
||||
!self.node.shouldCheckForMoved() &&
|
||||
!self.node.shouldCheckForReleased () {
|
||||
@ -280,8 +398,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override func mTouchesMoved(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
zoom.touchesMoved(touches)
|
||||
func touchesMoved(touchPoints: [MTouchEvent]) {
|
||||
if !self.node.shouldCheckForMoved() {
|
||||
return
|
||||
}
|
||||
@ -290,7 +407,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
let touchPoints = convert(touches: touches)
|
||||
touchesOfNode.keys.forEach { currentNode in
|
||||
guard let initialTouches = touchesOfNode[currentNode] else {
|
||||
return
|
||||
@ -324,30 +440,12 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override func mTouchesCancelled(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
touchesEnded(touches: touches)
|
||||
}
|
||||
|
||||
override func mTouchesEnded(_ touches: Set<MTouch>, with event: MEvent?) {
|
||||
touchesEnded(touches: touches)
|
||||
}
|
||||
|
||||
private func convert(touches: Set<MTouch>) -> [MTouchEvent] {
|
||||
return touches.map { touch -> MTouchEvent in
|
||||
let location = touch.location(in: self)
|
||||
let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque())
|
||||
return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id)
|
||||
}
|
||||
}
|
||||
|
||||
private func touchesEnded(touches: Set<MTouch>) {
|
||||
zoom.touchesEnded(touches)
|
||||
func touchesEnded(touchPoints: [MTouchEvent]) {
|
||||
guard let _ = renderer else {
|
||||
return
|
||||
}
|
||||
|
||||
let invertedViewPlace = self.place.invert()
|
||||
let touchPoints = convert(touches: touches)
|
||||
for touch in touchPoints {
|
||||
|
||||
touchesMap[touch]?.forEach { nodePath in
|
||||
@ -566,16 +664,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
|
||||
recognizersMap.removeValue(forKey: recognizer)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MGestureRecognizerDelegate
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldReceive touch: MTouch) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: MGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: MGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutHelper {
|
||||
|
@ -16,7 +16,7 @@ import AppKit
|
||||
|
||||
open class MacawZoom {
|
||||
|
||||
private var view: MView!
|
||||
private var view: MacawView!
|
||||
private var onChange: ((Transform) -> Void)!
|
||||
private var touches = [TouchData]()
|
||||
private var zoomData = ZoomData()
|
||||
@ -50,7 +50,7 @@ open class MacawZoom {
|
||||
onChange(zoomData.transform())
|
||||
}
|
||||
|
||||
func initialize(view: MView, onChange: @escaping ((Transform) -> Void)) {
|
||||
func initialize(view: MacawView, onChange: @escaping ((Transform) -> Void)) {
|
||||
self.view = view
|
||||
self.onChange = onChange
|
||||
}
|
||||
@ -81,6 +81,9 @@ open class MacawZoom {
|
||||
}
|
||||
|
||||
private func getNewZoom() -> ZoomData {
|
||||
if !trackMove && !trackScale && !trackRotate {
|
||||
return zoomData
|
||||
}
|
||||
if touches.isEmpty || (touches.count == 1 && !trackMove) {
|
||||
return zoomData
|
||||
}
|
||||
@ -93,15 +96,7 @@ open class MacawZoom {
|
||||
let e2 = touches[1].current(in: view)
|
||||
let scale = trackScale ? e1.distance(to: e2) / s1.distance(to: s2) : 1
|
||||
let a = trackRotate ? (e1 - e2).angle() - (s1 - s2).angle() : 0
|
||||
var offset = Size.zero
|
||||
if trackMove {
|
||||
let sina = sin(a)
|
||||
let cosa = cos(a)
|
||||
let w = e1.x - scale * (s1.x * cosa - s1.y * sina)
|
||||
let h = e1.y - scale * (s1.x * sina + s1.y * cosa)
|
||||
offset = Size(w: w, h: h)
|
||||
}
|
||||
return ZoomData(offset: offset, scale: scale, angle: a).combine(with: zoomData)
|
||||
return ZoomData(offset: .zero, scale: scale, angle: a).combine(with: zoomData)
|
||||
}
|
||||
|
||||
}
|
||||
@ -143,7 +138,7 @@ fileprivate class TouchData {
|
||||
let touch: MTouch
|
||||
let point: Point
|
||||
|
||||
convenience init(touch: MTouch, in view: MView) {
|
||||
convenience init(touch: MTouch, in view: MacawView) {
|
||||
self.init(touch: touch, point: touch.location(in: view).toMacaw())
|
||||
}
|
||||
|
||||
@ -152,7 +147,7 @@ fileprivate class TouchData {
|
||||
self.point = point
|
||||
}
|
||||
|
||||
func current(in view: MView) -> Point {
|
||||
func current(in view: MacawView) -> Point {
|
||||
return touch.location(in: view).toMacaw()
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,6 @@ class ShapeLayer: CAShapeLayer {
|
||||
return
|
||||
}
|
||||
|
||||
let renderContext = RenderContext(view: .none)
|
||||
renderContext.cgContext = ctx
|
||||
|
||||
renderer?.directRender(in: ctx, force: isForceRenderingEnabled)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user