Skip to content

Instantly share code, notes, and snippets.

@pkclsoft
Last active January 25, 2021 23:14
Show Gist options
  • Save pkclsoft/0801640933a7a12fbd3a65b3b47915f6 to your computer and use it in GitHub Desktop.
Save pkclsoft/0801640933a7a12fbd3a65b3b47915f6 to your computer and use it in GitHub Desktop.
SKUIViewWrapper.swift
//
/// 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