UIGraphicsBeginImageContext 阴影输出与 SwiftUI 视图阴影修饰符不同

UIGraphicsBeginImageContext shadow output is not same as SwiftUI view shadow modifier

提问人:Jabed Dhali 提问时间:11/15/2023 最后编辑:Jabed Dhali 更新时间:11/18/2023 访问量:21

问:

我想将一个图像拟合到另一个具有阴影效果的图像上。首先,我按如下方式构建 swiftUI 视图,然后尝试使用 UIGraphicsBeginImageContext 获得相同的输出。但不幸的是,这里发现了问题:输出图像中的阴影模糊与视图不同。我正在寻找合适的解决方案。

struct ContentView: View {
    @State private var imageRect : CGRect = .zero
    
    @State private var shadowBlur = 40.0
    @State private var shadowX = 100.0
    @State private var shadowY = 100.0
    
    var body: some View {
        VStack {
            GeometryReader(content: { geometry in
                ZStack {
                    Image("background")
                        .resizable()
                        .scaledToFit()
                        .overlay {
                            Image("foreground")
                                .resizable()
                                .scaledToFit()
                                .shadow(color: Color.red, radius: CGFloat(shadowBlur), x: shadowX, y: shadowY)
                        }
                        .clipped()
                        .background(GeometryGetter(rect: $imageRect))
                }
                .frame(width: geometry.size.width, height: geometry.size.height)
            })
            
            Button("Click") {
                let fgImage = UIImage(named: "foreground")!
                let bgImage = UIImage(named: "background")!
                
                let scale = bgImage.size.width / imageRect.width
                let fgFrame = fgImage.size.scaleSizeToFit(in: bgImage.size)
                let fggImage = fgImage.resize(targetSize: fgFrame)
                
                UIGraphicsBeginImageContext(bgImage.size)
                bgImage.draw(at: CGPoint.zero)
                let context = UIGraphicsGetCurrentContext()
                context?.setShadow(offset: CGSize(width: shadowX * scale, height: shadowY * scale), blur: shadowBlur * scale, color: UIColor.red.cgColor)
                let imageDrawPoint = CGPoint(x: bgImage.size.width/2.0 - fggImage.size.width / 2.0, y: bgImage.size.height/2.0 - fggImage.size.height / 2.0)
                fggImage.draw(at: imageDrawPoint)
                
                let outputImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                print("SUCCESS")
                
            }
        }
    }
}

extension UIImage {
    func resize(targetSize: CGSize) -> UIImage {
        let image = self.toCIImage()
        let scaleX = targetSize.width / image.extent.size.width
        let scaleY = targetSize.height / image.extent.size.height
        let scaleTransform = CGAffineTransform(scaleX: scaleX, y: scaleY)
        let scaledImage = image.transformed(by: scaleTransform)
        return scaledImage.toUIImage()
    }
}

extension CGSize {
    func scaleSizeToFit(in canvas: CGSize) -> CGSize {
        let widthScale = canvas.width / self.width
        let heightScale = canvas.height / self.height
        let scale = min(widthScale, heightScale)
        let scaledSize = CGSize(width: self.width * scale, height: self.height * scale)
        return scaledSize
    }
}

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { (g) -> Path in
            print("width: \(g.size.width), height: \(g.size.height)")
            DispatchQueue.main.async {
                self.rect = g.frame(in: .global)
            }
            return Path()
        }
    }
}
swift swiftui uikit shadow uigraphicscontext

评论

0赞 DonMag 11/15/2023
很难提供帮助,因为你没有向我们展示//......提供足够的代码,以便我们可以运行它并查看输出(查看最小可重现示例)。GeometryGetter(rect: $imageRect)fgImage.size.scaleSizeToFit(in: bgImage.size)fgImage.resize(targetSize: fgFrame)
0赞 Jabed Dhali 11/16/2023
感谢您的回复,对于没有提前提供详细信息,我深表歉意。变量 imageRect 通过 GeometryGetter 进行计算,并表示 UI 中 backgroundImage 的帧大小。scaleSizeToFit() 函数返回所需的帧大小,以确保在指定帧内正确适合。此外,resize() 函数负责将图像大小调整为目标大小。@DonMag,这些功能现已附加。

答:

1赞 DonMag 11/18/2023 #1

由于自动图像缩放,这可能无法为您提供确切的屏幕内容,但它会更接近......

您需要设置阴影模糊值以匹配屏幕缩放系数,因此请更改:

context?.setShadow(offset: CGSize(width: shadowX * scale, height: shadowY * scale),
                   blur: shadowBlur * scale,
                   color: UIColor.red.cgColor)

自:

context?.setShadow(offset: CGSize(width: shadowX * scale, height: shadowY * scale),
                   blur: shadowBlur * scale * UIScreen.main.scale,
                   color: UIColor.red.cgColor)

另一种选择是将视图本身()呈现为 -- 搜索会得到很多例子。ZStackUIImageHow to convert a SwiftUI view to a UIImage