Last active
January 25, 2021 23:14
-
-
Save pkclsoft/0801640933a7a12fbd3a65b3b47915f6 to your computer and use it in GitHub Desktop.
SKUIViewWrapper.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
/// SKUIViewWrapper.swift | |
/// | |
/// Created by Peter Easdown on 23/11/20. | |
/// | |
/// This is an attempt to port the class CCUIViewWrapper from Cocos2D to Swift and SpriteKit for use | |
/// in my apps as I move them from the excellent Cocos2D (OpenGL based) to SpriteKit (Metal based). | |
/// | |
/// Note that owing to the way the class manages the auto-adding and removal of the UIView, the wrapper | |
/// object should only be used once. If it is removed from the node tree, then you will need another instance | |
/// next time you want to add the same UIView object instance. | |
/// | |
import Foundation | |
import SpriteKit | |
class SKUIViewWrapper : SKNode { | |
/// The UIView object being wrapped by this SKNode. | |
var uiItem : UIView | |
/// An observer used to track when the node is added and removed from the node tree. | |
var observer: NSKeyValueObservation? | |
/// Initialises the object as a wrapper around a specific UIView object. | |
/// - Parameter ui: The UIView object to be wrapped. | |
init(forUIView ui: UIView) { | |
self.uiItem = ui | |
super.init() | |
// Add this observer so that the wrapper knows when it is added to the node tree, thus allowing it to also add the UIView it wraps to the underlying UIKit view hierarchy. | |
// | |
self.observer = self.observe(\.parent, options: [.new]) { [weak self] (wrapper, change) in | |
guard let `self` = self else { return } | |
if self.parent == nil { | |
// No parent anymore, so remove the UIView from the view hierarchy. | |
self.uiItem.removeFromSuperview() | |
} else { | |
// The node has been added to the node tree, so add the wrapped UIView to the view hierarchy, but only if it isn't already in the hierarchy. | |
if self.uiItem.superview == nil { | |
self.scene?.view?.addSubview(self.uiItem) | |
} | |
} | |
} | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
deinit { | |
// Ensure the parent observer is removed. | |
// | |
self.observer?.invalidate() | |
} | |
/// Transform the view to match the SKNode. | |
func updateUIViewTransform() { | |
var transform = CGAffineTransform(translationX: 0.0, y: SKUtilities.screenHeight()) | |
var p : SKNode? = self | |
while p != nil { | |
if let pp = p { | |
let thisAngle = pp.zRotation | |
if let ppp = pp.parent { | |
let pAngle = ppp.zRotation | |
transform = transform.translatedBy(x: (pp.position.x * CGFloat(cosf(Float(pAngle)))) + (pp.position.y * CGFloat(sinf(Float(pAngle)))), | |
y: (-pp.position.y * CGFloat(cosf(Float(pAngle)))) + (pp.position.x * CGFloat(sinf(Float(pAngle))))) | |
} else { | |
transform = transform.translatedBy(x: pp.position.x, y: -pp.position.y) | |
} | |
transform = transform.rotated(by: thisAngle) | |
transform = transform.scaledBy(x: pp.xScale, y: pp.yScale) | |
transform = transform.translatedBy(x: -pp.position.x, y: -pp.position.y) | |
} | |
p = p?.parent | |
} | |
self.uiItem.transform = transform | |
} | |
override var isHidden: Bool { | |
didSet { | |
self.uiItem.isHidden = isHidden | |
} | |
} | |
override var zRotation: CGFloat { | |
didSet { | |
self.updateUIViewTransform() | |
} | |
} | |
override var xScale: CGFloat { | |
didSet { | |
self.updateUIViewTransform() | |
} | |
} | |
override var yScale: CGFloat { | |
didSet { | |
self.updateUIViewTransform() | |
} | |
} | |
override var alpha: CGFloat { | |
didSet { | |
self.uiItem.alpha = alpha | |
} | |
} | |
override var position: CGPoint { | |
didSet { | |
self.updateUIViewTransform() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment