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

SwiftUI中如何根据用户输入尺寸在图片上绘制自定义透明框

问题需求

我想从本地存储选一张图片,显示在视图里后,让用户输入自定义的X、Y坐标以及宽高值,然后在图片上画出对应的矩形框。

已完成工作

已经实现了本地图片选择器功能。

尝试方案

试过用PencilKit在图片上方画框。

当前困境

没法根据用户输入的位置信息画出符合要求的框——能调出画布,但做不到自定义位置和尺寸的矩形。期望效果是根据用户输入的图片原点X、Y坐标和宽高绘制矩形框。

我现在用的是静态画框的代码,没法动态调整:

struct Rect: View {
    var body: some View {
        Path { path in
            let width: CGFloat = 30
            let height: CGFloat = 30
            // 1
            path.move(
                to: CGPoint(
                    x: 0 * width,
                    y: 0 * height
                )
            )
            // 2
            path.addLine(
                to: CGPoint(
                    x:  1 * width,
                    y: 0 * height)
            )
            // 3
            path.addLine(
                to: CGPoint(
                    x: 1 * width,
                    y: 1 * height)
            )
            path.addLine(
                to: CGPoint(
                    x: 0 * width,
                    y: 1 * height)
            )
            // 4
            path.closeSubpath()
        }
        .stroke(Color.red, lineWidth: 5)
    }
}

解决方案

不需要用PencilKit,直接用SwiftUI的Path结合状态变量绑定用户输入就能实现动态绘制。核心逻辑是用@State存储用户输入的参数,实时更新矩形的位置和尺寸,再通过ZStack将矩形叠加在图片上方。

完整示例代码:

import SwiftUI

struct ImageRectDrawer: View {
    // 存储选中的图片
    @State private var selectedImage: UIImage?
    // 用户输入的矩形参数,默认值可按需调整
    @State private var rectX: String = "50"
    @State private var rectY: String = "50"
    @State private var rectWidth: String = "100"
    @State private var rectHeight: String = "100"
    
    var body: some View {
        VStack(spacing: 20) {
            // 图片选择按钮(替换成你已实现的选择器逻辑)
            Button("选择图片") {
                // 这里调用你的图片选择器,将选中结果赋值给selectedImage
                selectedImage = UIImage(systemName: "photo") // 示例占位图
            }
            
            // 图片与矩形绘制区域
            if let image = selectedImage {
                ZStack {
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: .infinity, maxHeight: 400)
                    
                    // 动态生成矩形路径
                    Path { path in
                        // 处理输入转换,避免非数字输入崩溃
                        let x = CGFloat(Double(rectX) ?? 0)
                        let y = CGFloat(Double(rectY) ?? 0)
                        let width = CGFloat(Double(rectWidth) ?? 0)
                        let height = CGFloat(Double(rectHeight) ?? 0)
                        
                        // 直接添加矩形路径,替代手动绘制四条边
                        path.addRect(CGRect(x: x, y: y, width: width, height: height))
                    }
                    .stroke(Color.red, lineWidth: 5)
                }
                .padding()
            }
            
            // 用户输入表单
            VStack(spacing: 10) {
                HStack {
                    Text("X坐标:")
                    TextField("输入X", text: $rectX)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                }
                
                HStack {
                    Text("Y坐标:")
                    TextField("输入Y", text: $rectY)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                }
                
                HStack {
                    Text("宽度:")
                    TextField("输入宽度", text: $rectWidth)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                }
                
                HStack {
                    Text("高度:")
                    TextField("输入高度", text: $rectHeight)
                        .keyboardType(.decimalPad)
                        .textFieldStyle(.roundedBorder)
                }
            }
            .padding()
        }
        .padding()
    }
}

struct ImageRectDrawer_Previews: PreviewProvider {
    static var previews: some View {
        ImageRectDrawer()
    }
}

关键说明

  1. 动态参数绑定:@State变量与输入框双向绑定,输入内容变化时自动触发视图重绘,实时更新矩形。
  2. 路径简化:用path.addRect()直接生成矩形路径,比手动绘制四条边更简洁可靠。
  3. 容错处理:将输入字符串转为CGFloat时添加默认值,避免非数字输入导致崩溃。
  4. 坐标适配(可选):如果需要让用户输入的坐标对应原始图片尺寸,可通过GeometryReader获取图片显示后的缩放比例,对输入坐标进行转换:
// 在图片层添加GeometryReader获取显示尺寸
Image(uiImage: image)
    .resizable()
    .scaledToFit()
    .frame(maxWidth: .infinity, maxHeight: 400)
    .overlay(
        GeometryReader { geo in
            Color.clear
                .onAppear {
                    let imageSize = image.size
                    let displaySize = geo.size
                    let scaleX = displaySize.width / imageSize.width
                    let scaleY = displaySize.height / imageSize.height
                    // 后续用scaleX/scaleY转换用户输入的原始坐标
                }
        }
    )

内容的提问来源于stack exchange,提问作者Jigar Dave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 11:31:02