如何在UIDevice旋转后生成适配重定位节点的新UIBezierPath?
解决方案
1. 确保节点布局完成后再计算路径
viewDidLayoutSubviews是UIKit层的回调,SKView内部的节点布局可能未同步完成。计算路径前先强制SKView完成布局:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard let skView = view as? SKView else { return } // 强制SKView完成子节点布局 skView.layoutIfNeeded() // 仅在设备方向变化时执行更新,避免重复调用 let currentOrientation = UIDevice.current.orientation guard currentOrientation != previousOrientation else { return } previousOrientation = currentOrientation skView.isPaused = true // 更新节点位置与尺寸,确保基于当前view尺寸计算 updateSpriteNodesLayout() // 重新生成路径 setTrackPaths() skView.isPaused = false }
2. 修正createTrainPath的计算逻辑
确保路径计算时直接使用节点的实时属性,而非缓存的旧值:
func createTrainPath() -> UIBezierPath { let path = UIBezierPath() // 直接取当前节点的实时位置 let startNode = trainStartNode path.move(to: startNode.position) // 遍历节点时,实时获取每个节点的位置与尺寸 for node in trackNodes { path.addLine(to: node.position) // 基于节点当前尺寸绘制路径 path.addArc(withCenter: node.position, radius: node.size.width/2, startAngle: 0, endAngle: .pi*2, clockwise: true) } return path }
3. 确保路径被正确替换
如果是给SKShapeNode用路径,直接替换path属性;如果是用于节点跟随动作,需重新创建动作并替换:
func setTrackPaths() { let newPath = createTrainPath() // 更新路径显示节点 trackShapeNode.path = newPath.cgPath // 重新创建跟随动作,替换旧动作 let followAction = SKAction.follow(newPath.cgPath, asOffset: false, orientToPath: true, speed: 100) trainNode.removeAllActions() trainNode.run(followAction) }
4. 避免布局循环或重复触发
若更新节点布局时会再次触发viewDidLayoutSubviews,可添加标记控制:
private var isUpdatingLayout = false override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard !isUpdatingLayout else { return } isUpdatingLayout = true // 执行所有更新逻辑... isUpdatingLayout = false }
额外排查点
- 检查是否在路径计算中使用了固定的屏幕尺寸(如
UIScreen.main.bounds),旋转后应改用skView.bounds来计算节点位置。 - 若节点使用Auto Layout约束,确保旋转后约束已更新完成,或在
updateSpriteNodesLayout中手动基于新view尺寸计算位置。
内容的提问来源于stack exchange,提问作者user22530416
相关产品推荐
相关产品推荐

