微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在SwiftUI卡视图中将UIImage裁剪到跨设备的特定区域

如何解决在SwiftUI卡视图中将UIImage裁剪到跨设备的特定区域

我正在尝试在SwiftUI中制作卡片视图(非常类似于“今日应用故事”页面上的视图)。每张卡片都有一张图片,下面有一些文字,以及圆角。

每张卡图像均为600像素乘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,height: 400))!我找出了使我到达裁切后的板图像中心的常数(绝对不是最佳解决方案,但是我对如何将GeometryReader集成到此中一无所知–有什么想法吗?)

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

enter image description here

但是,如果我切换到iPhone SE(第二代),则平板不再位于卡的中央。可以预料,考虑到我对这些点和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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。