如何从 UIView 创建特定尺寸的图像

Ond*_*rol 3 uiview uiimage ios swift uigraphicsimagerenderer

我正在开发 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
}
Run Code Online (Sandbox Code Playgroud)

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

Ond*_*rol 5

所以实际上是的,在捕获图像之前我需要缩放整个视图及其子视图。这是我的发现(也许是显而易见的事情,但我花了一段时间才意识到 \xe2\x80\x93\xc2\xa0I\ 会对任何改进感到高兴)

\n\n

渲染相同尺寸的图像

\n\n

当你想将UIView捕获为图像时,你可以简单地使用这个函数。生成的图像将具有与视图相同的尺寸(根据实际设备缩放 2 倍/3 倍)

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

将视图渲染为图像 \xe2\x80\x93 相同大小

\n\n

不同尺寸的渲染图像

\n\n

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

\n\n

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

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

将视图渲染为更大尺寸的图像 \xe2\x80\x93

\n\n

我的方法

\n\n

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

\n\n

重要的提示!

\n\n

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

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

使用第二个视图将视图渲染为更大尺寸的图像 \xe2\x80\x93\n与实际使用的质量差异 \xe2\x80\x93 放大到标签:\n差异:之后/之前

\n