ios - 在 SwiftUI 卡 View 中跨设备将 UIImage 裁剪到特定区域

标签 ios swift swiftui uiimage uikit

我正在尝试在 SwiftUI 中创建一个卡片 View (与 App Story Today 页面上的 View 非常相似)。每张卡片都有一张图片、下面的一些文字和圆角边缘。

每张卡片图片大小为 600 x 400 像素。在每张图片上,都有一个我想要裁剪到的特定位置,而且它因图片而异。例如,在下图中(这是我在这篇文章中使用的图像),它大致位于盘子的中心(即当我裁剪卡片图像时,我想保留盘子而不是木头背景).但是裁剪原点/引用坐标因图像而异——对于某些图像,它可能在左侧、右侧等(现在我想到了,我该如何优化它?)。

enter image description here

这是我用来生成卡片的代码,以及我用来裁剪图像的代码:

//  StoryView.swift

import SwiftUI

struct StoryView: View {
    @Environment(\.colorScheme) var colorScheme
    
    var story: Story
    
    var body: some View {
                RoundedRectangle(cornerRadius: self.cornerRadius)
                    .fill(self.colorScheme == .light ? Color.white : self.darkModeCardColor)
                    .frame(height: self.cardHeight)
                    .shadow(radius: self.colorScheme == .light ? 20 : 0)
                    .overlay(imageAndText())
                    .padding([.leading, .trailing], horizontalSidePadding)
        }
        
    @ViewBuilder
    private func imageAndText() -> some View {
        VStack(spacing: 0) {
            Image(uiImage: self.croppedPrimaryImage)
                .resizable()
            
            // Spacer()
            
            HStack {
                VStack(alignment: .leading) {
                    Text("Lorem Ipsum".uppercased())
                        .font(.headline)
                        .foregroundColor(.secondary)
                    
                    Text(self.story.title)
                        .font(.title)
                        .fontWeight(.black)
                        .foregroundColor(.primary)
                        .lineLimit(2)
                        .padding([.vertical], 4)
                    
                    Text("Lorem ipsum dolor sit".uppercased())
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .layoutPriority(1)
                
                Spacer()
            }
            .padding()
        }
        .cornerRadius(self.cornerRadius)
    }

        
    // MARK: - Image Cropping
        
    // TODO: - Fix this so that there are no force unwrappings
    var croppedPrimaryImage: UIImage {
        cropImage(image: story.previewImage, toRect: CGRect(x: 85, y: 0, width: cardWidth, height: 400))!
    }
    
    // see: https://stackoverflow.com/questions/31254435/how-to-select-a-portion-of-an-image-crop-and-save-it-using-swift
    func cropImage(image: UIImage, toRect: CGRect) -> UIImage? {
        // Cropping is available through CGGraphics
        let cgImage :CGImage! = image.cgImage
        let croppedCGImage: CGImage! = cgImage.cropping(to: toRect)

        return UIImage(cgImage: croppedCGImage)
    }
    
    // MARK: - Drawing Constants
    
    private let cornerRadius: CGFloat = 30
    private let cardHeight: CGFloat = 450
    private let cardWidth: CGFloat = UIScreen.main.bounds.size.width
    private let horizontalSidePadding: CGFloat = 26
    private let darkModeCardColor = Color(red: 28/255, green: 28/255, blue: 30/255)
}


struct StoryView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            StoryView(story: Story(title: "Lorem ipsum", previewImage: UIImage(imageLiteralResourceName: "img.jpg")).colorScheme(.dark)
            
            // StoryView(story: Story(title: "Lorem ipsum dolor sit amet", previewImage: UIImage(imageLiteralResourceName: "img.jpg")).colorScheme(.light)
        }
    }
}

在这一行中,cropImage(image: story.previewImage, toRect: CGRect(x: 85, y: 0, width: cardWidth, height: 400))! 我想出了常量将我带到裁剪板图像的中心(绝对不是最好的解决方案,但我不知道如何将 GeometryReader 集成到其中 - 对此有什么想法吗?)

无论如何,如果我在 iPhone 11 Pro Max 上运行它,这张卡看起来很棒:

enter image description here

但是,如果我切换到 iPhone SE(第 2 代),则板不再位于卡片的中央。不出所料,鉴于我对点和 CGRect 进行了硬编码,但我该如何解决这个问题?

enter image description here

同样,我觉得有时我需要在这里使用 GeometryReader,我应该以像素为单位存储每张图像的中心坐标,然后处理它。

例如,此图像的裁剪坐标将是盘子中间的点,因此大约 (300, 200) 像素,然后我会添加和减去一定数量(基于可用空间(宽度和卡中的高度),具体取决于设备 - 我们通过几何阅读器获得此信息)从裁剪坐标的 x 和 y 坐标获取我为卡裁剪的图像。

我希望其中的一些内容有意义 - 如果您有任何想法可以帮助我,请告诉我。我很茫然。

最佳答案

只需使用尽可能少的硬编码和尽可能多的系统定义,然后自动布局就会适合每台设备。

这里是一些修改的部分(添加了 .scaledToFill.clipShape,并删除了硬编码)- 图像自然居中。使用 Xcode 12/iOS 14 进行演示和测试。

demo1 demo2 demo3

struct StoryView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
                RoundedRectangle(cornerRadius: self.cornerRadius)
                    .fill(self.colorScheme == .light ? Color.white : self.darkModeCardColor)
                    .frame(height: self.cardHeight)
                    .shadow(radius: self.colorScheme == .light ? 20 : 0)
                    .overlay(imageAndText())
                    .clipShape(RoundedRectangle(cornerRadius: self.cornerRadius))
                    .padding([.leading, .trailing])
        }

    @ViewBuilder
    private func imageAndText() -> some View {
        VStack(spacing: 0) {
            Image("img")
                .resizable()
                .scaledToFill()

            HStack {
                VStack(alignment: .leading) {
                    Text("Lorem Ipsum".uppercased())
                        .font(.headline)
                        .foregroundColor(.secondary)

                    Text("Lorem ipsum")
                        .font(.title)
                        .fontWeight(.black)
                        .foregroundColor(.primary)
                        .lineLimit(2)
                        .padding([.vertical], 4)

                    Text("Lorem ipsum dolor sit".uppercased())
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .layoutPriority(1)

                Spacer()
            }
            .padding()
        }
    }

//    ... other code

关于ios - 在 SwiftUI 卡 View 中跨设备将 UIImage 裁剪到特定区域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63680522/

相关文章:

ios - NSOperation 阻止 UI 绘制?

ios - 上传到S3存储桶Swift 3问题

swift - Quick-Nimble 框架中是否有等效的 XCTFail()?

ios - swift 中的 addsubview 命令后动画未运行

ios - swift 2.0 : Type of Expression is ambiguous without more context?

swift - 如何使用 SwiftUI 弹出到 Root View ?

ios - 在选项卡之间滑动时使 Capsule() 移动 -matchedGeometryEffect

ios - SwiftUI : ConfigurationError: noPreviewInfos 中的预览抛出错误

ios - SwiftUI:VStack 中的 VStack,标签被截断

ios - 无法在MDM中执行应用程序的安装