【问题标题】:How to create an image of specific size from UIView如何从 UIView 创建特定大小的图像
【发布时间】:2020-07-29 12:50:51
【问题描述】:

我正在开发 iOS 应用程序,该应用程序应使用户能够创建 Instagram 故事照片并将其导出到 Instagram。基本上是 Unfold、Stellar、Chroma Stories 之类的应用程序......我已经准备好了 UI,用户可以在其中从准备好的模板中进行选择,并向他们添加带有过滤器、标签等的自己的照片。

我的问题是 - 将创建的 UIView 导出到更大的图像的最佳方法是什么? 我的意思是如何获得最好的质量、清晰的标签像素等? 因为带有子视图(添加的照片、标签......)的模板视图占用了设备屏幕的 +- 一半。但我需要更大的尺寸来导出图像。 目前我使用:

 func makeImageFromView() -> UIImage {
    let format = UIGraphicsImageRendererFormat()
    let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)

    let renderer = UIGraphicsImageRenderer(size: size, format: format)
    let image = renderer.image { (ctx) in
        templateView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
    }
    return image
}

生成的图像大小为 1080 x 1920,但标签不清晰。 在将其制作成图像之前,我是否需要以某种方式缩放照片和字体大小? 谢谢!

【问题讨论】:

    标签: ios swift uiview uiimage uigraphicsimagerenderer


    【解决方案1】:

    所以实际上是的,在捕获图像之前我需要缩放整个视图和它的子视图。这是我的发现(可能是显而易见的事情,但我花了一段时间才意识到这一点——我会很高兴有任何改进)

    渲染相同大小的图片

    当您想将 UIView 捕获为图像时,您可以简单地使用此功能。生成的图像将具有与视图相同的大小(缩放 2x / 3x,具体取决于实际设备)

     func makeImageFrom(_ desiredView: MyView) -> UIImage {
        let size = CGSize(width: desiredView.bounds.width, height: desiredView.bounds.height)
        let renderer = UIGraphicsImageRenderer(size: size)
        let image = renderer.image { (ctx) in
            desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
        }
        return image
    }
    

    渲染不同尺寸的图片

    但是,当您想要导出图像的特定尺寸时该怎么办? 因此,从我的用例中,我想渲染最终尺寸(1080 x 1920)的图像,但我想捕获的视图尺寸较小(在我的情况下为 275 x 487)。如果你在没有任何东西的情况下进行这样的渲染,那么质量肯定会有所下降。

    如果您想避免这种情况并保留清晰的标签和其他子视图,则需要尝试将视图理想地缩放到所需的大小。在我的情况下,将其从 275 x 487 设置为 1080 x 1920。

    func makeImageFrom(_ desiredView: MyView) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        // We need to divide desired size with renderer scale, otherwise you get output size larger @2x or @3x
        let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
    
        let renderer = UIGraphicsImageRenderer(size: size, format: format)
        let image = renderer.image { (ctx) in
        // remake constraints or change size of desiredView to 1080 x 1920
        // handle it's subviews (update font size etc.)
        // ...
        desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
        // undo the size changes
        // ...
        }
        return image
    }
    

    我的方法

    但是因为我不想弄乱显示给用户的视图的大小,所以我采取了不同的方式并使用了第二个视图,它没有显示给用户。这意味着就在我想要捕获图像之前,我准备了具有相同内容但更大尺寸的“复制”视图。我没有将它添加到视图控制器的视图层次结构中,所以它是不可见的。

    重要提示!

    您确实需要注意子视图。这意味着,您必须增加字体大小、更新移动子视图的位置(例如它们的中心)等! 这里只用几行来说明这一点:

            // 1. Create bigger view
            let hdView = MyView()
            hdView.frame = CGRect(x: 0, y: 0, width: 1080, height: 1920)
    
            // 2. Load content according to the original view (desiredView)
            // set text, images...
    
            // 3. Scale subviews
            // Find out what scale we need
            let scaleMultiplier: CGFloat = 1080 / desiredView.bounds.width // 1080 / 275 = 3.927 ...
    
            // Scale everything, for examples label's font size
            [label1, label2].forEach { $0.font =  UIFont.systemFont(ofSize: $0.font.pointSize * scaleMultiplier, weight: .bold) }
            // or subview's center
            subview.center = subview.center.applying(.init(scaleX: scaleMultiplier, y: scaleMultiplier))
    
            // 4. Render image from hdView
            let hdImage = makeImageFrom(hdView)
    

    质量与实际使用的差异 - 放大到标签:

    【讨论】:

      猜你喜欢
      • 2011-01-30
      • 2023-03-22
      • 1970-01-01
      • 2013-07-17
      • 1970-01-01
      • 2014-12-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-22
      相关资源
      最近更新 更多