1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-11-04 00:39:57 +03:00

Merge pull request #671 from f3dm76/task/layer-animation-example

Add correct zoom-scroll handling
This commit is contained in:
Yuri Strot 2020-04-21 20:10:13 +07:00 committed by GitHub
commit 15027f0e53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 290 additions and 161 deletions

View File

@ -13,6 +13,9 @@ class AnimationsHierarchyViewController: UIViewController {
@IBOutlet weak var animView: MacawView! @IBOutlet weak var animView: MacawView!
var startCallbacks: [()->()] = []
var stopCallbacks: [()->()] = []
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -20,6 +23,22 @@ class AnimationsHierarchyViewController: UIViewController {
animView.zoom.enable() 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 { func createTree(height: Int) -> Node {
let rect = Rect(w: 10, h: 10) let rect = Rect(w: 10, h: 10)
@ -52,7 +71,14 @@ class AnimationsHierarchyViewController: UIViewController {
func createLeaf(childForm: Locus, xDelta: Double, yDelta: Double) -> Group { func createLeaf(childForm: Locus, xDelta: Double, yDelta: Double) -> Group {
let inset = childForm.bounds().w / 2 let inset = childForm.bounds().w / 2
let leaf = Shape(form: childForm, fill: Color.teal, place: .move(dx: -inset, dy: -inset)) 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)) let leafGroup = [leaf].group(place: .move(dx: xDelta, dy: yDelta))
leaf.onTap { _ in leaf.onTap { _ in

View File

@ -24,11 +24,8 @@ class BasicAnimation: Animation {
weak var node: Node? { weak var node: Node? {
didSet { didSet {
if !(self is CombineAnimation || self is AnimationSequence || self is EmptyAnimation) {
node?.animations.append(self) 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?() removeFunc?()
node?.animations.removeAll { $0 === self }
nodeRenderer?.freeLayer()
} }
override open func pause() { override open func pause() {
@ -115,6 +114,8 @@ class BasicAnimation: Animation {
} }
removeFunc?() removeFunc?()
node?.animations.removeAll { $0 === self }
nodeRenderer?.freeLayer()
} }
override func state() -> AnimationState { override func state() -> AnimationState {

View File

@ -143,9 +143,9 @@ class AnimationProducer {
} }
// MARK: - Sequence animation // MARK: - Sequence animation
func addAnimationSequence(_ animationSequnce: Animation, func addAnimationSequence(_ animationSequence: Animation,
_ context: AnimationContext) { _ context: AnimationContext) {
guard let sequence = animationSequnce as? AnimationSequence else { guard let sequence = animationSequence as? AnimationSequence else {
return return
} }
@ -198,7 +198,7 @@ class AnimationProducer {
} }
// MARK: - Stored animation // MARK: - Stored animation
func addStoredAnimations(_ node: Node, _ view: MacawView) { func addStoredAnimations(_ node: Node, _ view: DrawingView) {
addStoredAnimations(node, AnimationContext()) addStoredAnimations(node, AnimationContext())
} }

View File

@ -8,7 +8,7 @@ import AppKit
class AnimationUtils { 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 let node = renderer.node
if let cachedLayer = renderer.layer { if let cachedLayer = renderer.layer {

View File

@ -115,8 +115,8 @@ extension AnimationProducer {
// MARK: - Combine animation // MARK: - Combine animation
func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) { func addCombineAnimation(_ combineAnimation: Animation, _ context: AnimationContext) {
guard let combine = combineAnimation as? CombineAnimation, guard let combine = combineAnimation as? CombineAnimation,
let renderer = combine.nodeRenderer, let _ = combine.nodeRenderer,
let _ = renderer.view else { let node = combine.node else {
return return
} }
@ -176,6 +176,7 @@ extension AnimationProducer {
} }
combine.removeFunc = { combine.removeFunc = {
node.animations.removeAll { $0 === combine }
animations.forEach { animation in animations.forEach { animation in
animation.removeFunc?() animation.removeFunc?()
} }

View File

@ -30,7 +30,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte
let toLocus = morphingAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0) let toLocus = morphingAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration() 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 // Creating proper animation
let generatedAnimation = pathAnimation( let generatedAnimation = pathAnimation(
@ -94,6 +94,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte
let animationId = animation.ID let animationId = animation.ID
layer.add(generatedAnimation, forKey: animationId) layer.add(generatedAnimation, forKey: animationId)
animation.removeFunc = { [weak layer] in animation.removeFunc = { [weak layer] in
shape.animations.removeAll { $0 === animation }
layer?.removeAnimation(forKey: animationId) layer?.removeAnimation(forKey: animationId)
} }

View File

@ -47,6 +47,15 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex
node.opacityVar.value = opacityAnimation.getVFunc()(1.0) 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() renderer.freeLayer()
if !animation.cycled && if !animation.cycled &&
@ -58,10 +67,11 @@ func addOpacityAnimation(_ animation: BasicAnimation, _ context: AnimationContex
completion() completion()
} }
let layer = AnimationUtils.layerForNodeRenderer(renderer, context, animation: animation) let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation)
let animationId = animation.ID let animationId = animation.ID
layer.add(generatedAnimation, forKey: animationId) layer.add(generatedAnimation, forKey: animationId)
animation.removeFunc = { [weak layer] in animation.removeFunc = { [weak layer] in
node.animations.removeAll { $0 === animation }
layer?.removeAnimation(forKey: animationId) layer?.removeAnimation(forKey: animationId)
} }

View File

@ -30,7 +30,7 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0) let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration() 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 // Creating proper animation
let generatedAnimation = generateShapeAnimation(context, let generatedAnimation = generateShapeAnimation(context,

View File

@ -22,7 +22,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
let transactionsDisabled = CATransaction.disableActions() let transactionsDisabled = CATransaction.disableActions()
CATransaction.setDisableActions(true) 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 // Creating proper animation
let generatedAnimation = transformAnimationByFunc(transformAnimation, let generatedAnimation = transformAnimationByFunc(transformAnimation,
@ -54,6 +54,15 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
node.placeVar.value = transformAnimation.getVFunc()(1.0) 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() renderer.freeLayer()
if !animation.cycled && if !animation.cycled &&
@ -68,6 +77,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
let animationId = animation.ID let animationId = animation.ID
layer.add(generatedAnimation, forKey: animationId) layer.add(generatedAnimation, forKey: animationId)
animation.removeFunc = { [weak layer] in animation.removeFunc = { [weak layer] in
node.animations.removeAll { $0 === animation }
layer?.removeAnimation(forKey: animationId) layer?.removeAnimation(forKey: animationId)
} }

View File

@ -27,8 +27,8 @@ public extension MacawView {
y: -size.height / bounds.height y: -size.height / bounds.height
) )
context.cgContext = ctx drawingView.context.cgContext = ctx
renderer?.render(in: ctx, force: false, opacity: node.opacity) drawingView.renderer?.render(in: ctx, force: false, opacity: node.opacity)
ctx.endPDFPage() ctx.endPDFPage()
} }

View File

@ -13,7 +13,7 @@ class GroupRenderer: NodeRenderer {
return group return group
} }
init(group: Group, view: MacawView?, parentRenderer: GroupRenderer? = nil) { init(group: Group, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
self.group = group self.group = group
super.init(node: group, view: view, parentRenderer: parentRenderer) super.init(node: group, view: view, parentRenderer: parentRenderer)
updateRenderers() updateRenderers()

View File

@ -17,7 +17,7 @@ class ImageRenderer: NodeRenderer {
return image return image
} }
init(image: Image, view: MacawView?, parentRenderer: GroupRenderer? = nil) { init(image: Image, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
self.image = image self.image = image
super.init(node: image, view: view, parentRenderer: parentRenderer) super.init(node: image, view: view, parentRenderer: parentRenderer)
} }

View File

@ -22,7 +22,7 @@ class CachedLayer {
class NodeRenderer { class NodeRenderer {
weak var view: MacawView? weak var view: DrawingView?
var sceneLayer: CALayer? { var sceneLayer: CALayer? {
return view?.mLayer return view?.mLayer
} }
@ -71,7 +71,7 @@ class NodeRenderer {
fatalError("Unsupported") fatalError("Unsupported")
} }
init(node: Node, view: MacawView?, parentRenderer: GroupRenderer? = nil) { init(node: Node, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
self.view = view self.view = view
self.parentRenderer = parentRenderer self.parentRenderer = parentRenderer

View File

@ -5,10 +5,10 @@ import UIKit
#endif #endif
class RenderContext { class RenderContext {
weak var view: MacawView? weak var view: DrawingView?
var cgContext: CGContext? var cgContext: CGContext?
init(view: MacawView?) { init(view: DrawingView?) {
self.view = view self.view = view
self.cgContext = nil self.cgContext = nil
} }

View File

@ -16,7 +16,7 @@ class RenderUtils {
return p 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 { if let group = node as? Group {
return GroupRenderer(group: group, view: view, parentRenderer: parentRenderer) return GroupRenderer(group: group, view: view, parentRenderer: parentRenderer)
} else if let shape = node as? Shape { } else if let shape = node as? Shape {

View File

@ -10,7 +10,7 @@ class ShapeRenderer: NodeRenderer {
var shape: Shape var shape: Shape
init(shape: Shape, view: MacawView?, parentRenderer: GroupRenderer? = nil) { init(shape: Shape, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
self.shape = shape self.shape = shape
super.init(node: shape, view: view, parentRenderer: parentRenderer) super.init(node: shape, view: view, parentRenderer: parentRenderer)
} }

View File

@ -13,7 +13,7 @@ class TextRenderer: NodeRenderer {
return text return text
} }
init(text: Text, view: MacawView?, parentRenderer: GroupRenderer? = nil) { init(text: Text, view: DrawingView?, parentRenderer: GroupRenderer? = nil) {
self.text = text self.text = text
super.init(node: text, view: view, parentRenderer: parentRenderer) super.init(node: text, view: view, parentRenderer: parentRenderer)
} }

View File

@ -10,8 +10,196 @@ import AppKit
/// MacawView is a main class used to embed Macaw scene into your Cocoa UI. /// 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. /// You could create your own view extended from MacawView with predefined scene.
/// ///
open class MacawView: MView, MGestureRecognizerDelegate { 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 /// Scene root node
open var node: Node = Group() { open var node: Node = Group() {
didSet { didSet {
@ -42,8 +230,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
} }
} }
public let zoom = MacawZoom()
public var place: Transform { public var place: Transform {
return placeManager.placeVar.value return placeManager.placeVar.value
} }
@ -52,8 +238,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
return placeManager.placeVar return placeManager.placeVar
} }
private let placeManager = RootPlaceManager()
override open var frame: CGRect { override open var frame: CGRect {
didSet { didSet {
super.frame = frame 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() { override open func didMoveToSuperview() {
super.didMoveToSuperview() super.didMoveToSuperview()
@ -78,14 +270,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
animationProducer.addStoredAnimations(node, self) animationProducer.addStoredAnimations(node, self)
} }
override open var intrinsicContentSize: CGSize { private let placeManager = RootPlaceManager()
if let bounds = node.bounds {
return bounds.size().toCG()
} else {
return CGSize(width: MNoIntrinsicMetric(), height: MNoIntrinsicMetric())
}
}
private let layoutHelper = LayoutHelper() private let layoutHelper = LayoutHelper()
var touchesMap = [MTouchEvent: [NodePath]]() var touchesMap = [MTouchEvent: [NodePath]]()
@ -98,82 +283,17 @@ open class MacawView: MView, MGestureRecognizerDelegate {
var toRender = true var toRender = true
var frameSetFirstTime = false var frameSetFirstTime = false
#if os(OSX) func initializeView() {
open override var layer: CALayer? { self.contentLayout = .none
didSet { self.context = RenderContext(view: self)
guard self.layer != nil else {
return
} }
initializeView()
self.renderer = RenderUtils.createNodeRenderer(node, view: self) @objc public required init?(coder aDecoder: NSCoder) {
}
}
#endif
@objc public init?(node: Node, coder aDecoder: NSCoder) {
super.init(coder: aDecoder) 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) { public override init(frame: CGRect) {
super.init(frame: frame) 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() { open override func layoutSubviews() {
@ -194,12 +314,12 @@ open class MacawView: MView, MGestureRecognizerDelegate {
guard let renderer = renderer else { guard let renderer = renderer else {
return return
} }
renderer.calculateZPositionRecursively()
// TODO: actually we should track all changes // TODO: actually we should track all changes
placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw())) placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw()))
ctx.concatenate(self.place.toCG()) ctx.concatenate(self.place.toCG())
renderer.calculateZPositionRecursively()
renderer.render(in: ctx, force: false, opacity: node.opacity) renderer.render(in: ctx, force: false, opacity: node.opacity)
} }
@ -229,10 +349,8 @@ open class MacawView: MView, MGestureRecognizerDelegate {
} }
// MARK: - Touches // MARK: - Touches
override func mTouchesBegan(_ touches: Set<MTouch>, with event: MEvent?) { func touchesBegan(touchPoints: [MTouchEvent]) {
zoom.touchesBegan(touches)
let touchPoints = convert(touches: touches)
if !self.node.shouldCheckForPressed() && if !self.node.shouldCheckForPressed() &&
!self.node.shouldCheckForMoved() && !self.node.shouldCheckForMoved() &&
!self.node.shouldCheckForReleased () { !self.node.shouldCheckForReleased () {
@ -280,8 +398,7 @@ open class MacawView: MView, MGestureRecognizerDelegate {
} }
} }
override func mTouchesMoved(_ touches: Set<MTouch>, with event: MEvent?) { func touchesMoved(touchPoints: [MTouchEvent]) {
zoom.touchesMoved(touches)
if !self.node.shouldCheckForMoved() { if !self.node.shouldCheckForMoved() {
return return
} }
@ -290,7 +407,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
return return
} }
let touchPoints = convert(touches: touches)
touchesOfNode.keys.forEach { currentNode in touchesOfNode.keys.forEach { currentNode in
guard let initialTouches = touchesOfNode[currentNode] else { guard let initialTouches = touchesOfNode[currentNode] else {
return return
@ -324,30 +440,12 @@ open class MacawView: MView, MGestureRecognizerDelegate {
} }
} }
override func mTouchesCancelled(_ touches: Set<MTouch>, with event: MEvent?) { func touchesEnded(touchPoints: [MTouchEvent]) {
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)
guard let _ = renderer else { guard let _ = renderer else {
return return
} }
let invertedViewPlace = self.place.invert() let invertedViewPlace = self.place.invert()
let touchPoints = convert(touches: touches)
for touch in touchPoints { for touch in touchPoints {
touchesMap[touch]?.forEach { nodePath in touchesMap[touch]?.forEach { nodePath in
@ -566,16 +664,6 @@ open class MacawView: MView, MGestureRecognizerDelegate {
recognizersMap.removeValue(forKey: recognizer) 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 { class LayoutHelper {

View File

@ -16,7 +16,7 @@ import AppKit
open class MacawZoom { open class MacawZoom {
private var view: MView! private var view: MacawView!
private var onChange: ((Transform) -> Void)! private var onChange: ((Transform) -> Void)!
private var touches = [TouchData]() private var touches = [TouchData]()
private var zoomData = ZoomData() private var zoomData = ZoomData()
@ -50,7 +50,7 @@ open class MacawZoom {
onChange(zoomData.transform()) onChange(zoomData.transform())
} }
func initialize(view: MView, onChange: @escaping ((Transform) -> Void)) { func initialize(view: MacawView, onChange: @escaping ((Transform) -> Void)) {
self.view = view self.view = view
self.onChange = onChange self.onChange = onChange
} }
@ -81,6 +81,9 @@ open class MacawZoom {
} }
private func getNewZoom() -> ZoomData { private func getNewZoom() -> ZoomData {
if !trackMove && !trackScale && !trackRotate {
return zoomData
}
if touches.isEmpty || (touches.count == 1 && !trackMove) { if touches.isEmpty || (touches.count == 1 && !trackMove) {
return zoomData return zoomData
} }
@ -93,15 +96,7 @@ open class MacawZoom {
let e2 = touches[1].current(in: view) let e2 = touches[1].current(in: view)
let scale = trackScale ? e1.distance(to: e2) / s1.distance(to: s2) : 1 let scale = trackScale ? e1.distance(to: e2) / s1.distance(to: s2) : 1
let a = trackRotate ? (e1 - e2).angle() - (s1 - s2).angle() : 0 let a = trackRotate ? (e1 - e2).angle() - (s1 - s2).angle() : 0
var offset = Size.zero return ZoomData(offset: .zero, scale: scale, angle: a).combine(with: zoomData)
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)
} }
} }
@ -143,7 +138,7 @@ fileprivate class TouchData {
let touch: MTouch let touch: MTouch
let point: Point 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()) self.init(touch: touch, point: touch.location(in: view).toMacaw())
} }
@ -152,7 +147,7 @@ fileprivate class TouchData {
self.point = point self.point = point
} }
func current(in view: MView) -> Point { func current(in view: MacawView) -> Point {
return touch.location(in: view).toMacaw() return touch.location(in: view).toMacaw()
} }

View File

@ -17,9 +17,6 @@ class ShapeLayer: CAShapeLayer {
return return
} }
let renderContext = RenderContext(view: .none)
renderContext.cgContext = ctx
renderer?.directRender(in: ctx, force: isForceRenderingEnabled) renderer?.directRender(in: ctx, force: isForceRenderingEnabled)
} }
} }