簡體   English   中英

在 SwiftUI 卡視圖中跨設備將 UIImage 裁剪到特定區域

[英]Crop UIImage to specific area across devices in SwiftUI card view

我正在嘗試在 SwiftUI 中制作一個卡片視圖(非常類似於 App Story Today 頁面上的視圖)。每張卡片都有一個圖像、下面的一些文本和圓角邊緣。

每張卡片圖像為 600 x 400 像素。 在每張圖片上,都有一個我想要裁剪到的特定位置,而且它因圖片而異。 例如,在下圖中(這是我在這篇文章中使用的圖像),它大致位於盤子的中心(即當我裁剪卡片圖像時,我想保留盤子而不是木頭背景). 但是裁剪原點/參考坐標因圖像而異——對於某些圖像,它可能在左側、右側等(現在我想到了,我該如何優化它?)。

在此處輸入圖像描述

這是我用來生成我的卡片的代碼,以及我用來裁剪圖像的代碼:

//  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 上運行它,這張卡看起來很棒:

在此處輸入圖像描述

但是,如果我切換到 iPhone SE(第 2 代),則板不再位於卡的中央。 可以預料,鑒於我對點和 CGRect 進行了硬編碼,但我該如何解決這個問題?

在此處輸入圖像描述

同樣,我覺得有時我需要在這里使用 GeometryReader,我應該以像素為單位存儲每個圖像的中心坐標,然后再處理它。

例如,此圖像的裁剪坐標將是盤子中間的點,因此大約 (300, 200) 像素,然后我將添加和減去一定數量(基於可用空間(寬度和高度)卡,取決於設備——我們用幾何閱讀器得到它)從裁剪坐標的 x 和 y 坐標得到我為卡裁剪的圖像。

我希望其中一些是有道理的 - 如果您有任何想法可以幫助,請告訴我。 我不知所措。

只需使用盡可能少的硬編碼和盡可能多的系統定義,然后自動布局就會適合每個設備。

這是一些修改過的部分(添加了.scaledToFill.clipShape ,並刪除了硬編碼)——圖像自然居中。 使用 Xcode 12 / iOS 14 進行演示和測試。

演示1 演示2 演示3

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM