SpriteKit场景侧边模态呈现ViewController后变形问题排查求助
Looks like you've run into a layout quirk specific to iOS 11 and later when working with SpriteKit and modal view controllers. Let's break down why this is happening and walk through actionable fixes:
Why This Happens
iOS 11 introduced Safe Area layout guides, which changed how view frames are calculated during modal transitions. When you dismiss your side-presented modal, the main view's bounds might adjust, but your hardcoded SKView frame and static SKScene size aren't updating automatically. The fact that scrolling fixes it tells us your scroll handler is triggering a layout correction—we just need to replicate that fix for modal dismissals.
Step-by-Step Fixes
1. Use Auto Layout for SKView Instead of Hardcoded Frames
Right now you're initializing SKView with self.view.frame, which doesn't update when the view's bounds change (like after a modal dismiss). Switch to Auto Layout to ensure the SKView always matches the main view's size:
Replace this code:
let skView = SKView(frame: self.view.frame) skView.isUserInteractionEnabled = false skView.backgroundColor = .clear view.insertSubview(skView, belowSubview: collectionView)
With this:
let skView = SKView() skView.isUserInteractionEnabled = false skView.backgroundColor = .clear skView.translatesAutoresizingMaskIntoConstraints = false // Disable auto-resizing mask view.insertSubview(skView, belowSubview: collectionView) // Pin SKView to all edges of the main view NSLayoutConstraint.activate([ skView.topAnchor.constraint(equalTo: view.topAnchor), skView.leadingAnchor.constraint(equalTo: view.leadingAnchor), skView.trailingAnchor.constraint(equalTo: view.trailingAnchor), skView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ])
2. Set SKScene's Scale Mode to .resizeFill
Your WScene is initialized with the view's frame size, but without setting a scale mode, it won't adapt to size changes. Add .resizeFill to ensure the scene always matches the SKView's bounds:
wScene = WScene(size: view.bounds.size) wScene.scaleMode = .resizeFill // Critical for dynamic size updates wScene.backgroundColor = .clear
3. Sync Scene Size and Obstacle Position on Layout Changes
Add viewDidLayoutSubviews to your view controller to update the scene size and reposition the obstacle node whenever the view's layout changes (like after a modal dismiss):
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard let skView = view.subviews.first(where: { $0 is SKView }) as? SKView, let scene = skView.scene as? WScene else { return } // Update scene size if it doesn't match the SKView's current bounds if scene.size != skView.bounds.size { scene.size = skView.bounds.size // Reposition the obstacle node since the scene size changed if let buttonsObstacle = doubleButton?.buttonsView { let obstacleSize = buttonsObstacle.frame.size let convertedOrigin = view.convert(buttonsObstacle.center, from: buttonsObstacle.superview) let skConvertedOrigin = skView.convert(convertedOrigin, to: scene) scene.childNode(withName: WScene.obstacleNodeName)?.position = skConvertedOrigin } } }
4. Force Layout Update When Modal Dismisses
If you still see issues, explicitly trigger a layout update when the side modal is dismissed. Add a completion handler to your modal presentation:
// When presenting your side modal view controller let sideModalVC = YourSideModalViewController() sideModalVC.dismissCompletion = { [weak self] in guard let self = self else { return } self.view.setNeedsLayout() self.view.layoutIfNeeded() } present(sideModalVC, animated: true)
(Don't forget to add a dismissCompletion closure property to your side modal view controller and call it in viewWillDisappear or when the dismiss action is triggered.)
Why This Works
- Auto Layout ensures the
SKViewalways matches the main view's bounds, even after modal transitions. .resizeFillmakes theSKSceneautomatically adjust its size to fit theSKView.viewDidLayoutSubviewscatches any layout changes (like after a modal dismiss) and syncs the scene size and obstacle position.- The explicit layout update on modal dismiss guarantees the fix runs at the right time.
内容的提问来源于stack exchange,提问作者L.L

