You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift模拟器及预览黑屏问题求助(附无报错SwiftUI代码)

SwiftUI可交互线条容器黑屏问题修复

我编写了以下SwiftUI代码,期望实现一个带有可交互内部线条的容器,但该代码在模拟器和预览中均显示黑屏,且未出现任何报错信息。我是Swift新手,希望能得到帮助找出问题所在。

原代码

import SwiftUI

struct ContentView: View {
@State private var horizontalLines: [CGFloat] = [50, 100]
@State private var verticalLines: [CGFloat] = [100, 200]
@GestureState private var dragOffset: CGSize = CGSize.zero

var body: some View {
    GeometryReader { geometry in
        ZStack {
            Rectangle()
                .frame(width: geometry.size.width, height: geometry.size.height)
                .border(Color.black, width: 1)
            
            ForEach(horizontalLines, id: \.self) { y in
                LineView(startPoint: CGPoint(x: 0, y: y), endPoint: CGPoint(x: geometry.size.width, y: y))
                    .gesture(horizontalDragGesture(y: y))
            }
            
            ForEach(verticalLines, id: \.self) { x in
                LineView(startPoint: CGPoint(x: x, y: 0), endPoint: CGPoint(x: x, y: geometry.size.height))
                    .gesture(verticalDragGesture(x: x))
            }
        }
        .background(Color.white)
        .gesture(containerDragGesture(geometry: geometry))
    }
    .edgesIgnoringSafeArea(.all)
}

private func horizontalDragGesture(y: CGFloat) -> some Gesture {
    DragGesture()
        .updating($dragOffset) { value, state, _ in
            state = value.translation
        }
        .onChanged { value in
            let newY = y + value.translation.height
            guard let index = horizontalLines.firstIndex(of: y) else { return }
            horizontalLines[index] = newY
        }
}

private func verticalDragGesture(x: CGFloat) -> some Gesture {
    DragGesture()
        .updating($dragOffset) { value, state, _ in
            state = value.translation
        }
        .onChanged { value in
            let newX = x + value.translation.width
            guard let index = verticalLines.firstIndex(of: x) else { return }
            verticalLines[index] = newX
        }
}

private func containerDragGesture(geometry: GeometryProxy) -> some Gesture {
    DragGesture()
        .updating($dragOffset) { value, state, _ in
            state = value.translation
        }
        .onEnded { value in
            let offsetX = value.translation.width
            let offsetY = value.translation.height
            let containerRightBorder = geometry.size.width + offsetX
            let containerBottomBorder = geometry.size.height + offsetY
            
            if let rightMostLineX = verticalLines.last, containerRightBorder < rightMostLineX {
                verticalLines[verticalLines.count - 1] = containerRightBorder
            }
            
            if let bottomMostLineY = horizontalLines.last, containerBottomBorder < bottomMostLineY {
                horizontalLines[horizontalLines.count - 1] = containerBottomBorder
            }
        }
}

struct LineView: View {
    var startPoint: CGPoint
    var endPoint: CGPoint
    
    var body: some View {
        Path { path in
            path.move(to: startPoint)
            path.addLine(to: endPoint)
        }
        .stroke(Color.black, lineWidth: 2)
      }
    }
  }

  struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
    ContentView()
   }
 }

实际显示为全黑屏幕。


问题分析与修复

黑屏直接原因

Rectangle()默认使用黑色填充,它被放置在ZStack的最底层且占据整个屏幕尺寸,覆盖了ZStack的白色背景,导致屏幕显示全黑。

其他潜在问题

除了黑屏,代码还存在手势冲突、视图重建异常、拖拽逻辑错误等问题,以下是完整修复后的代码及细节说明:

修复后代码

import SwiftUI

// 自定义结构体存储线条位置,避免用数值作为id的问题
struct LinePosition: Identifiable {
    let id = UUID()
    var value: CGFloat
}

struct ContentView: View {
    @State private var horizontalLines: [LinePosition] = [.init(value: 50), .init(value: 100)]
    @State private var verticalLines: [LinePosition] = [.init(value: 100), .init(value: 200)]
    // 每个手势使用独立的GestureState,避免冲突
    @GestureState private var horizontalDragOffset: CGSize = .zero
    @GestureState private var verticalDragOffset: CGSize = .zero
    @GestureState private var containerDragOffset: CGSize = .zero

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                // 修复1:设置透明填充,只保留边框
                Rectangle()
                    .fill(Color.clear)
                    .frame(width: geometry.size.width, height: geometry.size.height)
                    .border(Color.black, width: 1)
                
                // 修复2:使用Identifiable结构体,避免id变化导致视图重建
                ForEach($horizontalLines) { $line in
                    LineView(
                        startPoint: CGPoint(x: 0, y: line.value + horizontalDragOffset.height),
                        endPoint: CGPoint(x: geometry.size.width, y: line.value + horizontalDragOffset.height)
                    )
                    .gesture(horizontalDragGesture(for: $line))
                }
                
                ForEach($verticalLines) { $line in
                    LineView(
                        startPoint: CGPoint(x: line.value + verticalDragOffset.width, y: 0),
                        endPoint: CGPoint(x: line.value + verticalDragOffset.width, y: geometry.size.height)
                    )
                    .gesture(verticalDragGesture(for: $line))
                }
            }
            .background(Color.white)
            .gesture(containerDragGesture(geometry: geometry))
        }
        .edgesIgnoringSafeArea(.all)
    }

    private func horizontalDragGesture(for line: Binding<LinePosition>) -> some Gesture {
        DragGesture()
            .updating($horizontalDragOffset) { value, state, _ in
                state = value.translation
            }
            .onEnded { value in
                // 修复3:拖拽结束后更新最终位置,避免重复累加
                line.value += value.translation.height
            }
    }

    private func verticalDragGesture(for line: Binding<LinePosition>) -> some Gesture {
        DragGesture()
            .updating($verticalDragOffset) { value, state, _ in
                state = value.translation
            }
            .onEnded { value in
                line.value += value.translation.width
            }
    }

    private func containerDragGesture(geometry: GeometryProxy) -> some Gesture {
        DragGesture()
            .updating($containerDragOffset) { value, state, _ in
                state = value.translation
            }
            .onEnded { value in
                let offsetX = value.translation.width
                let offsetY = value.translation.height
                let containerRightBorder = geometry.size.width + offsetX
                let containerBottomBorder = geometry.size.height + offsetY
                
                if let lastLine = verticalLines.last, containerRightBorder < lastLine.value {
                    verticalLines[verticalLines.count - 1].value = containerRightBorder
                }
                
                if let lastLine = horizontalLines.last, containerBottomBorder < lastLine.value {
                    horizontalLines[horizontalLines.count - 1].value = containerBottomBorder
                }
            }
    }

    struct LineView: View {
        var startPoint: CGPoint
        var endPoint: CGPoint
        
        var body: some View {
            Path { path in
                path.move(to: startPoint)
                path.addLine(to: endPoint)
            }
            .stroke(Color.black, lineWidth: 2)
            // 修复4:扩大可触摸区域,提升拖拽体验
            .contentShape(Rectangle())
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

修复细节

  • 解决黑屏:给Rectangle设置fill(Color.clear),仅保留黑色边框,不再遮挡白色背景。
  • 避免视图重建:用LinePosition结构体(实现Identifiable)存储线条位置,UUID作为唯一标识,修改位置时不会触发视图销毁重建。
  • 解决手势冲突:为水平拖拽、垂直拖拽、容器拖拽分别分配独立的@GestureState变量,避免状态更新混乱。
  • 优化拖拽逻辑:将线条最终位置更新移至onEnded回调,避免拖拽过程中重复累加位移;用GestureState实时更新显示位置,保证拖拽流畅。
  • 提升交互体验:给LineView添加contentShape(Rectangle()),扩大可触摸区域,让拖拽操作更顺手。

内容的提问来源于stack exchange,提问作者Maria Grigoryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:14:56