在 SwiftUI 卡视图中跨设备将 UIImage 裁剪到特定区域
Crop UIImage to specific area across devices in SwiftUI card view
我正在尝试在 SwiftUI 中制作一个卡片视图(非常类似于 App Story Today 页面上的视图)。每张卡片都有一张图片,下面有一些文字,还有圆边。
每张卡片图片大小为 600 x 400 像素。在每张图片上,都有一个我想要裁剪到的特定位置,而且它因图片而异。例如,在下图中(这是我为此使用的图像 post),它大致位于盘子的中心(即当我裁剪卡片图像时,我想保留盘子但是不是木头背景)。但是裁剪 origin/reference 坐标因图像而异——对于某些图像,它可能在左侧、右侧等(现在我想起来了,我该如何优化它?)。
这是我用来生成卡片的代码,以及我用来裁剪图像的代码:
// 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:
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)像素,然后我会加减一定的量(根据可用的space(宽度和高度)在卡片中,取决于设备 - 我们从裁剪坐标的 x 和 y 坐标中使用几何 reader) 得到它,以获得卡片的裁剪图像。
我希望其中的一些内容有意义 - 如果您有任何想法可以帮助,请告诉我。我很茫然。
尽可能少地使用 hard-codings 并尽可能多地使用 system-defined,然后 auto-layout 将适合所有设备。
这是一些修改的部分(添加了 .scaledToFill
和 .clipShape
,并删除了硬编码)- 图像自然居中。使用 Xcode 12 / iOS 14.
进行演示和测试
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
我正在尝试在 SwiftUI 中制作一个卡片视图(非常类似于 App Story Today 页面上的视图)。每张卡片都有一张图片,下面有一些文字,还有圆边。
每张卡片图片大小为 600 x 400 像素。在每张图片上,都有一个我想要裁剪到的特定位置,而且它因图片而异。例如,在下图中(这是我为此使用的图像 post),它大致位于盘子的中心(即当我裁剪卡片图像时,我想保留盘子但是不是木头背景)。但是裁剪 origin/reference 坐标因图像而异——对于某些图像,它可能在左侧、右侧等(现在我想起来了,我该如何优化它?)。
这是我用来生成卡片的代码,以及我用来裁剪图像的代码:
// 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:
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)像素,然后我会加减一定的量(根据可用的space(宽度和高度)在卡片中,取决于设备 - 我们从裁剪坐标的 x 和 y 坐标中使用几何 reader) 得到它,以获得卡片的裁剪图像。
我希望其中的一些内容有意义 - 如果您有任何想法可以帮助,请告诉我。我很茫然。
尽可能少地使用 hard-codings 并尽可能多地使用 system-defined,然后 auto-layout 将适合所有设备。
这是一些修改的部分(添加了 .scaledToFill
和 .clipShape
,并删除了硬编码)- 图像自然居中。使用 Xcode 12 / iOS 14.
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