Swift UIScrollView 中的 SKSprite 节点
Swift SKSpritenodes in a UIScrollView
好的,我正在尝试将 SKSpriteNode
添加到 UIScrollView
,我该怎么做?
到目前为止你尝试了什么?
作为一般的堆栈溢出规则,如果您需要帮助,您应该始终 post 编码。
我建议始终从我的 gitHub 项目中获取此代码的最新版本,以防我根据此答案做出更改,link 位于底部。
第 1 步:创建一个新的 swift 文件并粘贴此代码
import SpriteKit
/// Scroll direction
enum ScrollDirection {
case vertical // cases start with small letters as I am following Swift 3 guildlines.
case horizontal
}
class CustomScrollView: UIScrollView {
// MARK: - Static Properties
/// Touches allowed
static var disabledTouches = false
/// Scroll view
private static var scrollView: UIScrollView!
// MARK: - Properties
/// Current scene
private let currentScene: SKScene
/// Moveable node
private let moveableNode: SKNode
/// Scroll direction
private let scrollDirection: ScrollDirection
/// Touched nodes
private var nodesTouched = [AnyObject]()
// MARK: - Init
init(frame: CGRect, scene: SKScene, moveableNode: SKNode) {
self.currentScene = scene
self.moveableNode = moveableNode
self.scrollDirection = scrollDirection
super.init(frame: frame)
CustomScrollView.scrollView = self
self.frame = frame
delegate = self
indicatorStyle = .White
scrollEnabled = true
userInteractionEnabled = true
//canCancelContentTouches = false
//self.minimumZoomScale = 1
//self.maximumZoomScale = 3
if scrollDirection == .horizontal {
let flip = CGAffineTransformMakeScale(-1,-1)
transform = flip
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Touches
extension CustomScrollView {
/// Began
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches began in current scene
currentScene.touchesBegan(touches, withEvent: event)
/// Call touches began in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesBegan(touches, withEvent: event)
}
}
}
/// Moved
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches moved in current scene
currentScene.touchesMoved(touches, withEvent: event)
/// Call touches moved in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesMoved(touches, withEvent: event)
}
}
}
/// Ended
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches ended in current scene
currentScene.touchesEnded(touches, withEvent: event)
/// Call touches ended in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesEnded(touches, withEvent: event)
}
}
}
/// Cancelled
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
for touch in touches! {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches cancelled in current scene
currentScene.touchesCancelled(touches, withEvent: event)
/// Call touches cancelled in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesCancelled(touches, withEvent: event)
}
}
}
}
// MARK: - Touch Controls
extension CustomScrollView {
/// Disable
class func disable() {
CustomScrollView.scrollView?.userInteractionEnabled = false
CustomScrollView.disabledTouches = true
}
/// Enable
class func enable() {
CustomScrollView.scrollView?.userInteractionEnabled = true
CustomScrollView.disabledTouches = false
}
}
// MARK: - Delegates
extension CustomScrollView: UIScrollViewDelegate {
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollDirection == .horizontal {
moveableNode.position.x = scrollView.contentOffset.x
} else {
moveableNode.position.y = scrollView.contentOffset.y
}
}
}
这会创建一个 UIScrollView 的子class 并设置它的基本属性。它有自己的 touches 方法,可以传递给相关场景。
第 2 步:在您想要使用它的相关场景中,您创建一个滚动视图和可移动节点 属性 就像这样
weak var scrollView: CustomScrollView!
let moveableNode = SKNode()
并在 didMoveToView
中将它们添加到场景中
scrollView = CustomScrollView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height), scene: self, moveableNode: moveableNode, scrollDirection: .vertical)
scrollView.contentSize = CGSizeMake(self.frame.size.width, self.frame.size.height * 2)
view?.addSubview(scrollView)
addChild(moveableNode)
您在第 1 行中所做的是使用场景尺寸初始化滚动视图助手。您还传递场景以供参考和您在步骤 2 中创建的 moveableNode。
第 2 行是设置 scrollView 内容大小的地方,在本例中它是屏幕高度的两倍。
第 3 步:- 添加标签或节点等并定位它们。
label1.position.y = CGRectGetMidY(self.frame) - self.frame.size.height
moveableNode.addChild(label1)
在此示例中,标签将位于滚动视图的第二页上。这是您必须使用标签和定位的地方。
我建议,如果你在滚动视图中有很多页面和很多标签,请执行以下操作。为滚动视图中的每个页面创建一个 SKSpriteNode,并使每个页面的大小与屏幕相同。将它们称为 page1Node、page2Node 等。您可以将第二页上的所有标签添加到 page2Node。这里的好处是您基本上可以像往常一样将所有内容放置在 page2Node 中,而不仅仅是将 page2Node 放置在滚动视图中。
你也很幸运,因为垂直使用滚动视图(你说你想要的)你不需要做任何翻转和反向定位。
我制作了一些 class 功能,因此如果您需要禁用 scrollView,以防在 scrollView 上覆盖另一个菜单。
CustomScrollView.enable()
CustomScrollView.disable()
最后不要忘记在转换到新场景之前从场景中删除滚动视图。在 spritekit 中处理 UIKit 时的痛点之一。
scrollView?.removeFromSuperView()
你 运行 遇到了一个我纠结了一段时间的问题。根本问题是您不能将 SKNode 添加到 UIKit 视图,也不能将 UIKit 视图添加到 SKNode。视图不是节点,节点也不是视图,遗憾的是这行不通。虽然您可以将 UIKit 元素添加到 SKScene 的视图 属性,但这是您所能接受的范围。在我终于明白之前,我为此苦苦挣扎了一段时间。
我仍然混合使用 UIKit 和 SpriteKit,顺便说一句,但我这样做的方式有限,它从不依赖于向视图添加节点,反之亦然。
您最好的选择是创建某种 SKNode,它的大小与您需要的一样大,可以容纳所有 SKSpriteNode,然后在 touchesMoved: 函数期间调整其在屏幕上的位置。 (本质上是创建您自己的基于 SpriteKit 的滚动视图)。
您可以使用 SKCameraNode
和比视口大的场景。这基本上是滚动视图的 SpriteKit 版本。
好的,我正在尝试将 SKSpriteNode
添加到 UIScrollView
,我该怎么做?
到目前为止你尝试了什么? 作为一般的堆栈溢出规则,如果您需要帮助,您应该始终 post 编码。
我建议始终从我的 gitHub 项目中获取此代码的最新版本,以防我根据此答案做出更改,link 位于底部。
第 1 步:创建一个新的 swift 文件并粘贴此代码
import SpriteKit
/// Scroll direction
enum ScrollDirection {
case vertical // cases start with small letters as I am following Swift 3 guildlines.
case horizontal
}
class CustomScrollView: UIScrollView {
// MARK: - Static Properties
/// Touches allowed
static var disabledTouches = false
/// Scroll view
private static var scrollView: UIScrollView!
// MARK: - Properties
/// Current scene
private let currentScene: SKScene
/// Moveable node
private let moveableNode: SKNode
/// Scroll direction
private let scrollDirection: ScrollDirection
/// Touched nodes
private var nodesTouched = [AnyObject]()
// MARK: - Init
init(frame: CGRect, scene: SKScene, moveableNode: SKNode) {
self.currentScene = scene
self.moveableNode = moveableNode
self.scrollDirection = scrollDirection
super.init(frame: frame)
CustomScrollView.scrollView = self
self.frame = frame
delegate = self
indicatorStyle = .White
scrollEnabled = true
userInteractionEnabled = true
//canCancelContentTouches = false
//self.minimumZoomScale = 1
//self.maximumZoomScale = 3
if scrollDirection == .horizontal {
let flip = CGAffineTransformMakeScale(-1,-1)
transform = flip
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Touches
extension CustomScrollView {
/// Began
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches began in current scene
currentScene.touchesBegan(touches, withEvent: event)
/// Call touches began in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesBegan(touches, withEvent: event)
}
}
}
/// Moved
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches moved in current scene
currentScene.touchesMoved(touches, withEvent: event)
/// Call touches moved in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesMoved(touches, withEvent: event)
}
}
}
/// Ended
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches ended in current scene
currentScene.touchesEnded(touches, withEvent: event)
/// Call touches ended in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesEnded(touches, withEvent: event)
}
}
}
/// Cancelled
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
for touch in touches! {
let location = touch.locationInNode(currentScene)
guard !CustomScrollView.disabledTouches else { return }
/// Call touches cancelled in current scene
currentScene.touchesCancelled(touches, withEvent: event)
/// Call touches cancelled in all touched nodes in the current scene
nodesTouched = currentScene.nodesAtPoint(location)
for node in nodesTouched {
node.touchesCancelled(touches, withEvent: event)
}
}
}
}
// MARK: - Touch Controls
extension CustomScrollView {
/// Disable
class func disable() {
CustomScrollView.scrollView?.userInteractionEnabled = false
CustomScrollView.disabledTouches = true
}
/// Enable
class func enable() {
CustomScrollView.scrollView?.userInteractionEnabled = true
CustomScrollView.disabledTouches = false
}
}
// MARK: - Delegates
extension CustomScrollView: UIScrollViewDelegate {
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollDirection == .horizontal {
moveableNode.position.x = scrollView.contentOffset.x
} else {
moveableNode.position.y = scrollView.contentOffset.y
}
}
}
这会创建一个 UIScrollView 的子class 并设置它的基本属性。它有自己的 touches 方法,可以传递给相关场景。
第 2 步:在您想要使用它的相关场景中,您创建一个滚动视图和可移动节点 属性 就像这样
weak var scrollView: CustomScrollView!
let moveableNode = SKNode()
并在 didMoveToView
中将它们添加到场景中scrollView = CustomScrollView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height), scene: self, moveableNode: moveableNode, scrollDirection: .vertical)
scrollView.contentSize = CGSizeMake(self.frame.size.width, self.frame.size.height * 2)
view?.addSubview(scrollView)
addChild(moveableNode)
您在第 1 行中所做的是使用场景尺寸初始化滚动视图助手。您还传递场景以供参考和您在步骤 2 中创建的 moveableNode。 第 2 行是设置 scrollView 内容大小的地方,在本例中它是屏幕高度的两倍。
第 3 步:- 添加标签或节点等并定位它们。
label1.position.y = CGRectGetMidY(self.frame) - self.frame.size.height
moveableNode.addChild(label1)
在此示例中,标签将位于滚动视图的第二页上。这是您必须使用标签和定位的地方。
我建议,如果你在滚动视图中有很多页面和很多标签,请执行以下操作。为滚动视图中的每个页面创建一个 SKSpriteNode,并使每个页面的大小与屏幕相同。将它们称为 page1Node、page2Node 等。您可以将第二页上的所有标签添加到 page2Node。这里的好处是您基本上可以像往常一样将所有内容放置在 page2Node 中,而不仅仅是将 page2Node 放置在滚动视图中。
你也很幸运,因为垂直使用滚动视图(你说你想要的)你不需要做任何翻转和反向定位。
我制作了一些 class 功能,因此如果您需要禁用 scrollView,以防在 scrollView 上覆盖另一个菜单。
CustomScrollView.enable()
CustomScrollView.disable()
最后不要忘记在转换到新场景之前从场景中删除滚动视图。在 spritekit 中处理 UIKit 时的痛点之一。
scrollView?.removeFromSuperView()
你 运行 遇到了一个我纠结了一段时间的问题。根本问题是您不能将 SKNode 添加到 UIKit 视图,也不能将 UIKit 视图添加到 SKNode。视图不是节点,节点也不是视图,遗憾的是这行不通。虽然您可以将 UIKit 元素添加到 SKScene 的视图 属性,但这是您所能接受的范围。在我终于明白之前,我为此苦苦挣扎了一段时间。
我仍然混合使用 UIKit 和 SpriteKit,顺便说一句,但我这样做的方式有限,它从不依赖于向视图添加节点,反之亦然。
您最好的选择是创建某种 SKNode,它的大小与您需要的一样大,可以容纳所有 SKSpriteNode,然后在 touchesMoved: 函数期间调整其在屏幕上的位置。 (本质上是创建您自己的基于 SpriteKit 的滚动视图)。
您可以使用 SKCameraNode
和比视口大的场景。这基本上是滚动视图的 SpriteKit 版本。