swiftui - 如何在用户调整大小和重新定位后从 SwiftUI 2.0 滚动视图中保存完成的图像
问题描述
使用 Swift 2.0,我希望在用户从滚动视图 (ZoomScrollView) 中选择他们希望如何在框架中看到它之后,找到一种方法来捕获调整大小的图像。
我知道 Swift 有一些复杂的例子,但我希望在 Swift 2.0 中找到一种更简单的方法来捕捉它。在我所有的搜索中,我都听说过使用 ZStack 和一些遮罩或覆盖,但找不到一个简单的好例子。
我希望有人可以用 ZStack、掩码等更新我的示例,以及如何提取图像以保存或提供更好的示例。
import SwiftUI
struct ContentView: View {
@Environment(\.presentationMode) var presentationMode
@State var isAccepted: Bool = false
@State var isShowingImagePicker = false
@State var isShowingActionPicker = false
@State var sourceType:UIImagePickerController.SourceType = .camera
@State var image:UIImage?
var body: some View {
HStack {
Color(UIColor.systemYellow).frame(width: 8)
VStack(alignment: .leading) {
HStack {
Spacer()
VStack {
if image != nil {
ZoomScrollView {
Image(uiImage: image!)
.resizable()
.scaledToFit()
}
.frame(width: 300, height: 300, alignment: .center)
.clipShape(Circle())
} else {
Image(systemName: "person.crop.circle")
.resizable()
.font(.system(size: 32, weight: .light))
.frame(width: 300, height: 300, alignment: .center)
.cornerRadius(180)
.foregroundColor(Color(.systemGray))
.clipShape(Circle())
}
}
Spacer()
}
Spacer()
HStack {
Button(action: {
self.isShowingActionPicker = true
}, label: {
Text("Select Image")
.foregroundColor(.blue)
})
.frame(width: 130)
.actionSheet(isPresented: $isShowingActionPicker, content: {
ActionSheet(title: Text("Select a profile avatar picture"), message: nil, buttons: [
.default(Text("Camera"), action: {
self.isShowingImagePicker = true
self.sourceType = .camera
}),
.default(Text("Photo Library"), action: {
self.isShowingImagePicker = true
self.sourceType = .photoLibrary
}),
.cancel()
])
})
.sheet(isPresented: $isShowingImagePicker) {
imagePicker(image: $image, isShowingImagePicker: $isShowingImagePicker ,sourceType: self.sourceType)
}
Spacer()
// Save button
Button(action: {
// Save Image here... print for now just see if file dimensions are the right size
print("saved: ", image!)
self.presentationMode.wrappedValue.dismiss()
}
) {
HStack {
Text("Save").foregroundColor(isAccepted ? .gray : .blue)
}
}
.frame(width: 102)
.padding(.top)
.padding(.bottom)
//.buttonStyle(RoundedCorners())
.disabled(isAccepted) // Disable if if already isAccepted is true
}
}
Spacer()
Color(UIColor.systemYellow).frame(width: 8)
}
.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
.background(Color(UIColor.systemYellow))
}
}
struct ZoomScrollView<Content: View>: UIViewRepresentable {
private var content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
func makeUIView(context: Context) -> UIScrollView {
// set up the UIScrollView
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator // for viewForZooming(in:)
scrollView.maximumZoomScale = 20
scrollView.minimumZoomScale = 1
scrollView.bouncesZoom = true
// create a UIHostingController to hold our SwiftUI content
let hostedView = context.coordinator.hostingController.view!
hostedView.translatesAutoresizingMaskIntoConstraints = true
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostedView.frame = scrollView.bounds
scrollView.addSubview(hostedView)
return scrollView
}
func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: self.content))
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
// update the hosting controller's SwiftUI content
context.coordinator.hostingController.rootView = self.content
assert(context.coordinator.hostingController.view.superview == uiView)
}
// MARK: - Coordinator
class Coordinator: NSObject, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>
init(hostingController: UIHostingController<Content>) {
self.hostingController = hostingController
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return hostingController.view
}
}
}
struct imagePicker:UIViewControllerRepresentable {
@Binding var image: UIImage?
@Binding var isShowingImagePicker: Bool
typealias UIViewControllerType = UIImagePickerController
typealias Coordinator = imagePickerCoordinator
var sourceType:UIImagePickerController.SourceType = .camera
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.delegate = context.coordinator
return picker
}
func makeCoordinator() -> imagePickerCoordinator {
return imagePickerCoordinator(image: $image, isShowingImagePicker: $isShowingImagePicker)
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
class imagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
@Binding var image: UIImage?
@Binding var isShowingImagePicker: Bool
init(image:Binding<UIImage?>, isShowingImagePicker: Binding<Bool>) {
_image = image
_isShowingImagePicker = isShowingImagePicker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let uiimage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
image = uiimage
isShowingImagePicker = false
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShowingImagePicker = false
}
}
只想返回在圆圈中放大的图像。图像可以是方形的(re:300x300 帧),这很好,只需要缩放图像而不是整个屏幕或原始图像。
解决方案
根据评论,以下更改是成功的:
添加以下状态变量:
@State private var rect: CGRect = .zero
@State private var uiimage: UIImage? = nil // resized image
选择图像后,将“RectGetter”添加到拾取的图像帧
if image != nil {
ZoomScrollView {
Image(uiImage: image!)
.resizable()
.scaledToFit()
}
.frame(width: 300, height: 300, alignment: .center)
.clipShape(Circle())
.background(RectGetter(rect: $rect))
这是我添加的结构和扩展
extension UIView {
func asImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
struct RectGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { proxy in
self.createView(proxy: proxy)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
最后我将图像设置为保存
self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect)
这假定为根控制器。但是,在我的生产应用程序中,我必须指向 self
self.uiimage = UIApplication.shared.windows[0].self.asImage(rect: self.rect)
然后我能够保存该图像。
一些笔记。返回的图像是很好的矩形。然而,由于捕获图像的方式,圆形cropShape之外的其余矩形具有背景颜色。在这种情况下,圆圈外的角处为黄色。可能有一种方法可以让某种 ZOrder 蒙版在您调整图像大小时覆盖图像以进行显示,但随后会访问正确的图层并保存完整的矩形图片。如果有人想进一步提出建议,那将是一个更清洁的解决方案,但前提是您将始终以保存时相同的裁剪形状显示图片。
推荐阅读
- ansible - Ansible jenkins_job 为 Jenkins-Crumb 添加头信息
- powershell - 使用 Powershell 创建文件夹并根据以下条件放置文件
- amcharts4 - AMCharts - 如何为堆积柱形图中的每个类别指定配色方案?
- r - 请解决R studio的这个问题:没有为“dense_3_input”提供数据。需要每个键的数据:['dense_3_input']
- html - 在 div 内垂直对齐图像和跨度
- r - 连续评估一个条件,如果条件满足则返回某个值
- c# - 未知操作时如何等待 Task.Run(action)
- java - 从 Spring Boot 资源执行和处理 Void @Async 操作
- eslint - 导入``,ESLint:无法读取null的属性“范围” linting时发生
- python - 对于给定的时间戳,在数据框中查找具有最接近时间戳的行的索引