Имитация UIScrollView в SpriteKit: прокрутка SKScene работает, когда SKView имеет высоту 2000 точек, но не работает, когда высота составляет 2050 - PullRequest
0 голосов
/ 30 августа 2018

Цель - прокрутить SKScene с тем же поведением, что и UIScrollView.

Другие вопросы похожи, но ответы не помогли.

Этот код иллюстрирует, как UIScrollView + SKView будет отображать контент, когда подпредставление SKView имеет высоту 2000 точек, но ничего не показывает, если подпредставление имеет высоту 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)
    }
}

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Эти два класса демонстрируют, как прокручивать SKScene меню или сцены с поведением UIScrollView.

Спасибо @KnightofDragon за обнаружение ключевого недостатка в коде.

Это разочаровывает, что Apple не предоставляет эту функциональность изначально, но, надеюсь, никому больше не придется тратить дни на эксперименты с лучшим способом реализации прокрутки меню в SpriteKit!

UIViewController предполагает, что UIScrollView был добавлен через раскадровку.

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)
    }
}
0 голосов
/ 31 августа 2018

См. SpriteKit: почему SKView исчезает на одной высоте, а появляется на другой высоте? Какая максимальная высота для SKView? в зависимости от вашего размера.

Что касается того, что делать со своим представлением прокрутки, так как вы ограничены размером при рендеринге openGL, вам нужно подложить вашу сцену за вашим просмотром прокрутки (не делать это подпредставлением), а затем передать координату прокрутки в сцену, и установите положение сцен соответственно. Теперь это может отключить прикосновение к вашей сцене, поэтому вам придется настроить его так, чтобы оно попадало в вид позади него.

...