[英]SwiftUI: Round the corners of a UIImage?
我正在使用此扩展程序拍摄 SwiftUI 视图的图像(用于共享),但唯一的问题是我的视图的cornerRadius
为 10.0,因此圆角,因此该图像最终以黑色非圆角结束,我如何获得摆脱它?
extension View {
func takeScreenshot(origin: CGPoint, size: CGSize) -> UIImage? {
// Get the main window.
guard let window = UIApplication.shared.windows.first else {
print("View.takeScreenshot: No main window found")
return nil
}
// Create an image of the entire window. Note how we're using `window.bounds` for this
// to capture the entire window.
UIGraphicsBeginImageContextWithOptions(window.bounds.size, false, 0.0)
let renderer = UIGraphicsImageRenderer(bounds: window.bounds, format: UIGraphicsImageRendererFormat())
let image = renderer.image { (context) in
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
UIGraphicsEndImageContext()
/*
At this point we have a screenshot of the entire window.
Now we're going to crop it to just include the part of the screen
we want.
*/
// Scale is the pixel density of the screen. E.g. 3.0 on iPhone 12 Pro which has a 3x display.
// This will be used in the UIImage extension below.
let scale = UIScreen.main.scale
let rect = CGRect(x: origin.x, y: origin.y, width: size.width, height: size.height)
let croppedImage = image.cropped(boundingBox: rect, scale: scale)
return croppedImage
}
}
extension UIImage {
func cropped(boundingBox: CGRect, scale: CGFloat) -> UIImage? {
/*
To crop UIImage we must first convert it to a CGImage.
UIImage uses points, which are independent of pixels.
Therefore, we need to take the scaling factor of the screen into account
when cropping.
For example, if we want to crop a 100x50pt square starting at (75, 90) from a UIImage
on a device with a 2x scaling factor, we would multiple everything by 2 and crop a
200x100px square starting at (150, 180).
*/
let x = boundingBox.origin.x * scale
let y = boundingBox.origin.y * scale
let width = boundingBox.width * scale
let height = boundingBox.height * scale
let adjustedBoundingBox = CGRect(x: x, y: y, width: width, height: height)
guard let cgImage = self.cgImage?.cropping(to: adjustedBoundingBox) else {
print("UIImage.cropped: Couldn't create cropped image")
return nil
}
return UIImage(cgImage: cgImage)
}
}
我正在截图的视图:
var shareCard: some View {
VStack {
Spacer()
GeometryReader { geometry in
if backgroundImage != UIImage() {
Image(uiImage: backgroundImage)
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped() //needed to add clipped otherwise the picture would go outside of the frame. From https://sarunw.com/posts/how-to-resize-swiftui-image-and-keep-aspect-ratio/
.cornerRadius(10.0)
.overlay(
Color.black
.cornerRadius(10.0)
.opacity(0.45) //keep at 0.45?
)
} else {
Image("sampleBackground")
.resizable()
//.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.cornerRadius(10.0)
.onAppear {
proxy = geometry
}
}
}
Spacer()
}
.frame(height: 375)
.padding(.horizontal)
.overlay(
TabView(selection: $selectedTabIndex) {
//Omitted - these are views that are overlayed over the background or image and don't impact the size of the snapshot
}
.frame(height: 430)
.padding(.horizontal)
.tabViewStyle(.page(indexDisplayMode: isTakingSnapShot ? .never : .always))
.overlay(
VStack {
switch shareType {
case .TodaySummary:
VStack {
HStack {
HStack(alignment: .center) {
Image("logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 40)
.padding(.leading)
VStack(alignment: .leading, spacing: 3) {
Text("AppName")
.foregroundColor(.white)
.font(.headline)
.fontWeight(.bold)
Text(Date(), style: .date)
.foregroundColor(.white)
.font(.footnote)
}
}
Spacer()
}
.padding([.leading, .top])
Spacer()
}
.frame(height: 375)
case .Workout:
EmptyView() //Pass empty view here because we use a different header for workout share
}
}
)
)
}
正如我在评论中所说,我对 SwiftUI 所做的工作很少——但是,这可能会帮助您继续前进。
使用运行此代码的代码:
点击红色按钮捕获并保存此图像:
我试图通过给主视图一个黄色背景来让它变得明显,这样我们就可以看到透明的圆角。 当然,您在页面上看不到它,但是如果您下载上面的图像(或运行以下代码并亲自尝试),您会看到它具有透明度。
您发布的代码中有太多未知数,因此我声明了自己的testCard
视图。 一切都应该非常简单。
import SwiftUI
struct CaptureView: View {
var testCard: some View {
VStack {
Spacer()
GeometryReader { geometry in
Image("sampleBackground")
.resizable()
.frame(width: geometry.size.width, height: geometry.size.height)
.cornerRadius(20.0)
}
Spacer()
}
.frame(width: 320, height: 240)
.padding(.horizontal)
.overlay(
VStack {
VStack {
HStack {
HStack(alignment: .center) {
Image("logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 40)
.padding(.leading)
VStack(alignment: .leading, spacing: 3) {
Text("AppName")
.foregroundColor(.white)
.font(.headline)
.fontWeight(.bold)
Text(Date(), style: .date)
.foregroundColor(.white)
.font(.footnote)
}
}
Spacer()
}
.padding([.leading, .top])
Spacer()
}
.frame(height: 220)
}
)
}
var body: some View {
VStack {
Spacer()
Text("Tap red Button to capture view")
Spacer()
testCard
Spacer()
Button("Capture testCard View") {
let img = testCard.snapshot(atSize: CGSize(width: 320.0, height: 240.0))
saveImage(img)
}
.padding()
.foregroundColor(.white)
.background(Color.red)
.cornerRadius(40)
Spacer()
}
.background(Color(red: 1.0, green: 1.0, blue: 0.0))
}
func saveImage(_ img: UIImage) {
var s: String = "Results:\n\n"
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fName: String = "testCard.png"
let url = documents.appendingPathComponent(fName)
if let data = img.pngData() {
do {
try data.write(to: url)
} catch {
s += "Unable to Write Image Data to Disk"
print(s)
return
}
} else {
s += "Could not get png data"
print(s)
return
}
s += "Logical Size: \(img.size)\n"
s += "Scale: \(img.scale)\n"
s += "Actual Size: \(CGSize(width: img.size.width * img.scale, height: img.size.height * img.scale))\n"
s += "File \"\(fName)\" saved to Documents folder\n"
print(s)
// print the path to documents in debug console
// so we can copy/paste into Finder to get to the files
print(documents.path)
}
}
extension View {
// if view HAS a valid .intrinsicContentSize
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
// specify size to save
func snapshot(atSize targetSize: CGSize) -> UIImage {
let controller = UIHostingController(rootView: self, ignoreSafeArea: true)
let view = controller.view
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
// extension to remove safe area from UIHostingController
// source: https://stackoverflow.com/a/70339424/6257435
extension UIHostingController {
convenience public init(rootView: Content, ignoreSafeArea: Bool) {
self.init(rootView: rootView)
if ignoreSafeArea {
disableSafeArea()
}
}
func disableSafeArea() {
guard let viewClass = object_getClass(view) else { return }
let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea")
if let viewSubclass = NSClassFromString(viewSubclassName) {
object_setClass(view, viewSubclass)
}
else {
guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return }
guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return }
if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) {
let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in
return .zero
}
class_addMethod(viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), method_getTypeEncoding(method))
}
objc_registerClassPair(viewSubclass)
object_setClass(view, viewSubclass)
}
}
}
struct CaptureView_Previews: PreviewProvider {
static var previews: some View {
CaptureView()
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.