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

SwiftUI ForEach循环内指定Cell单独截图功能异常问题求助

SwiftUI ForEach循环中指定Cell截图解决方案

核心问题梳理

  • 触发时序错误:点击截图按钮时立即清空了menuIndex,导致PrintableCells中监听takePic变化的逻辑判断tap.menuIndex == index直接失效,截图代码未执行
  • 截图逻辑错误:原有截图方法是重新生成View实例渲染,不是截取当前屏幕已展示的Cell区域,内容和实际显示不一致
  • 视图层级错误:半透明遮罩写在ForEach循环内部,点击任意Cell会触发所有Cell生成遮罩,多层叠加导致显示异常

修正后完整可运行代码

import SwiftUI

class ScreenShotViewModel: ObservableObject {
    @Published var openMenu: Bool = false
    @Published var menuIndex: Int? = nil
    @Published var takePic: Bool = false
}

struct Home: View {
    @StateObject private var tap = ScreenShotViewModel()
    
    var body: some View {
        ZStack {
            ScrollView {
                ForEach(1..<21) { index in
                    PrintableCells(index: index)
                        .environmentObject(tap)
                        .onTapGesture {
                            tap.openMenu.toggle()
                            tap.menuIndex = index
                        }
                }
            }
            // 遮罩和按钮移到外部,避免多层叠加
            if tap.openMenu {
                Color.black.opacity(0.75)
                    .ignoresSafeArea()
                    .onTapGesture {
                        tap.openMenu = false
                        tap.menuIndex = nil
                    }
                
                if let selectIndex = tap.menuIndex {
                    ScreenShotButton(index: selectIndex)
                        .environmentObject(tap)
                }
            }
        }
    }
}

struct PrintableCells: View {
    @EnvironmentObject var tap: ScreenShotViewModel
    let index: Int
    
    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .fill(Color.blue.opacity(0.5))
            .frame(height: 200)
            .overlay(
                Text("ScreenShot: \(index)")
                    .font(.title3)
                    .fontWeight(.bold)
            )
            .background(
                GeometryReader { proxy in
                    Color.clear
                        .onChange(of: tap.takePic) { newVal in
                            guard newVal, tap.menuIndex == index else { return }
                            // 截图当前屏幕对应Cell区域
                            let image = UIScreen.main.screenShot(in: proxy.frame(in: .global))
                            print("截取到第\(index)个Cell的截图:\(image)")
                            // 截图完成后再清空状态
                            tap.takePic = false
                            tap.openMenu = false
                            tap.menuIndex = nil
                        }
                }
            )
    }
}

struct ScreenShotButton: View {
    @EnvironmentObject var tap: ScreenShotViewModel
    let index: Int
    
    var body: some View {
        ZStack {
            Button {
                // 只触发截图,状态清空放到截图完成后执行
                tap.takePic.toggle()
            } label: {
                VStack {
                    Image(systemName: "camera.viewfinder")
                    Text("SnapShot Cell: \(index)")
                }
            }
        }
        .padding(15)
        .background(Color.gray.opacity(0.8))
        .cornerRadius(15)
        .shadow(color: .green, radius: 10, x: 0, y: 0)
    }
}

// 新增屏幕指定区域截图方法
extension UIScreen {
    func screenShot(in rect: CGRect) -> UIImage {
        guard let window = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .compactMap({$0 as? UIWindowScene})
                .first?.windows
                .first(where: \.isKeyWindow) else { return UIImage() }
        
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
        window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
        let fullImage = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
        UIGraphicsEndImageContext()
        
        // 裁剪指定区域
        guard let cgImage = fullImage.cgImage?.cropping(to: rect) else { return UIImage() }
        return UIImage(cgImage: cgImage, scale: UIScreen.main.scale, orientation: fullImage.imageOrientation)
    }
}

功能验证

点击任意Cell会弹出对应序号的截图按钮,点击按钮后会自动截取当前选中Cell的内容,控制台会打印截图对象,可直接按需存入系统相册或做其他业务处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 17:48:05