在SpriteKit中模拟UIScrollView:滚动SKScene在SKView高达2000点但在高度为2050时失败时有效

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在SpriteKit中模拟UIScrollView:滚动SKScene在SKView高达2000点但在高度为2050时失败时有效相关的知识,希望对你有一定的参考价值。

目标是使用与SKScene相同的行为滚动UIScrollView

其他问题类似,但答案没有帮助。

此代码说明了当SKView子视图高度为2000点时UIScrollView + SKView将如何显示内容,但如果子视图高度为2050点,则不会显示任何内容。

高度很重要,例如,如果SKScene包含3000点高的节点地图,我们需要在UIScrollView中渲染的场景(通过在SKView中呈现场景,其本身是UIScrollView的子视图)。

class TestViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {
        // Call super
        super.viewDidLoad()

        // Make scene (then someone call the police)
        let scene = TestScene(size: UIScreen.main.bounds.size)

        // Allow other overlays to get presented
        definesPresentationContext = true

        // Set background color
        scrollView.backgroundColor = UIColor.clear

        // Create content view for scrolling since SKViews vanish with height > 2050
        let contentHeight = scene.getScrollHeight()
        let contentFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: contentHeight)
        let contentView = UIView(frame: contentFrame)
        contentView.backgroundColor = UIColor.clear

        // Create SKView
        let skFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: contentHeight)
        let skView = SKView(frame: skFrame)

        // Configure <scrollView>
        scrollView.insertSubview(skView, at: 0)
        scrollView.addSubview(contentView)
        scrollView.delegate = self
        scrollView.contentSize = contentFrame.size

        // Present scene
        skView.presentScene(scene)
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        scene.scrollBy(offset: scrollView.contentOffset.y)
    }
}
答案

有关您的尺寸问题,请参阅SpriteKit: why does SKView disappear at one height but appears at another height? What's the maximum height for SKView?

至于如何处理滚动视图,因为你只能在openGL渲染上调整大小,你需要做的是将你的场景置于滚动视图后面(不要使它成为子视图)然后将滚动坐标位置传递给场景,并相应地设置场景位置。现在,这可能会禁用场景中的触摸,因此您必须将其设置为触及其背后的视图。

另一答案

这两个类演示了如何使用SKScene行为滚动UIScrollView菜单或场景。

感谢@KnightofDragon捕获代码中的关键缺陷。

令人沮丧的是苹果本身并不提供这种功能,但希望没有人不得不浪费时间在SpriteKit上尝试实现菜单滚动的最佳方法!

UIViewController假设UIScrollView已经通过Storyboard添加。

class ScrollViewController: UIViewController, UIScrollViewDelegate {
    // IB Outlets
    @IBOutlet weak var scrollView: UIScrollView!

    // General Vars
    var scene = ScrollScene()

    // =======================================================================================================
    // MARK: Public Functions
    // =======================================================================================================
    override func viewDidLoad() {
        // Call super
        super.viewDidLoad()

        // Create scene
        scene = ScrollScene()

        // Allow other overlays to get presented
        definesPresentationContext = true

        // Create content view for scrolling since SKViews vanish with height > ~2048
        let contentHeight = scene.getScrollHeight()
        let contentFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: contentHeight)
        let contentView = UIView(frame: contentFrame)
        contentView.backgroundColor = UIColor.clear

        // Create SKView with same frame as <scrollView>, must manually compute because <scrollView> frame not ready at this point
        let scrollViewPosY = CGFloat(0)
        let scrollViewHeight = UIScreen.main.bounds.size.height - scrollViewPosY
        let scrollViewFrame = CGRect(x: 0, y: scrollViewPosY, width: UIScreen.main.bounds.size.width, height: scrollViewHeight)
        let skView = SKView(frame: scrollViewFrame)
        view.insertSubview(skView, at: 0)

        // Configure <scrollView>
        scrollView.addSubview(contentView)
        scrollView.delegate = self
        scrollView.contentSize = contentFrame.size

        // Present scene
        skView.presentScene(scene)

        // Handle taps on <scrollView>
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(scrollViewDidTap))
        scrollView.addGestureRecognizer(tapGesture)
    }

    // =======================================================================================================
    // MARK: UIScrollViewDelegate Functions
    // =======================================================================================================
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        scene.scrollBy(contentOffset: scrollView.contentOffset.y)
    }


    // =======================================================================================================
    // MARK: Gesture Functions
    // =======================================================================================================
    @objc func scrollViewDidTap(_ sender: UITapGestureRecognizer) {
        let scrollViewPoint = sender.location(in: sender.view!)
        scene.viewDidTapPoint(viewPoint: scrollViewPoint, contentOffset: scrollView.contentOffset.y)
    }
}



class ScrollScene : SKScene {
    // Layer Vars
    let scrollLayer = SKNode()

    // General Vars
    var originalPosY = CGFloat(0)


    // ================================================================================================
    // MARK: Initializers
    // ================================================================================================
    override init() {
        super.init()
    }


    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    // ================================================================================================
    // MARK: Public Functions
    // ================================================================================================
    func scrollBy(contentOffset: CGFloat) {
        scrollLayer.position.y = originalPosY + contentOffset
    }


    func viewDidTapPoint(viewPoint: CGPoint, contentOffset: CGFloat) {
        let nodes = getNodesTouchedFromView(point: viewPoint, contentOffset: contentOffset)
    }


    func getScrollHeight() -> CGFloat {
        return scrollLayer.calculateAccumulatedFrame().height
    }


    fileprivate func getNodesTouchedFromView(point: CGPoint, contentOffset: CGFloat) -> [SKNode] {
        var scenePoint = convertPoint(fromView: point)
        scenePoint.y += contentOffset
        return scrollLayer.nodes(at: scenePoint)
    }
}

以上是关于在SpriteKit中模拟UIScrollView:滚动SKScene在SKView高达2000点但在高度为2050时失败时有效的主要内容,如果未能解决你的问题,请参考以下文章

Sprite Kit中的UIScrollView水平滚动菜单

SpriteKit 场景图像在模拟器上显示为红十字

SpriteKit 和可滚动列表

在 UIScrollView 中模拟拖动结束

有没有办法在 UIScrollView 中模拟 scrollViewDidScrollToBottom?

SPRITEKIT游戏框架之关于PHYSICS物理引擎属性