如何在 SwiftUI 中居中裁剪图像

问题描述 投票:0回答:3

我是 SwiftUI 新手。我想大家都到了这个阶段。我已经成为应用程序开发人员大约 6 年了,我觉得在 StackOverflow 上问这个问题很愚蠢。但我到处都看了。如何在 SwiftUI 中的 ImageView 中居中裁剪图像?

我知道可以选择更改纵横比,但我只看到适合和填充。我只是想让 imageView 将图像居中裁剪(android 术语)。有谁知道吗

ios image swiftui center crop
3个回答
101
投票

Android 的

ImageView.ScaleType
文档
CENTER_CROP
描述为:

中心_裁剪

均匀缩放图像(保持图像的长宽比),以便 图像的两个尺寸(宽度和高度)将等于或 大于视图的相应尺寸(减去填充)。 然后图像在视图中居中。

这本质上就是Aspect Fill Scaling(又名

.scaledToFill()
)的作用,除了(令人惊讶的是)Aspect Fill不会剪掉落在框架之外的部分。

通过制作图像

.resizable
,并应用
.scaledToFill()
。图像将按比例缩放以填充其可用框架,并根据需要保留顶部和底部或侧面。
.clipped()
然后删除图像在框架之外的部分。
.contentShape(Rectangle())
将命中测试限制在该区域,
.clipped()

没有改变
Image("myImage")
    .resizable()
    .scaledToFill()
    .frame(width: 200, height: 200, alignment: .center)
    .clipped()
    .contentShape(Rectangle())

为了更方便,我创建了这个

extension
Image
:

extension Image {
    func centerCropped() -> some View {
        GeometryReader { geo in
            self
            .resizable()
            .scaledToFill()
            .frame(width: geo.size.width, height: geo.size.height)
            .clipped()
            .contentShape(Rectangle())
        }
    }
}

要使用

Image
extension
,只需将其放入项目中的文件中即可(像
image-centercropped.swift
这样的名称会很好用)。 然后只需将
.centerCropped()
添加到您想要居中裁剪的任何图像即可。

Image("apolloimage").centerCropped()

它使用

GeometryReader
来计算其框架,以便它可以正确裁剪图像,这意味着 you 不必指定框架即可获得正确的裁剪。 您可以使用显式框架自由调整图像大小,或者仅添加
padding()
Spacer()
来保持其相对于其他用户界面项目的良好放置。

例如:如果您希望图像填满手机屏幕:

struct ContentView: View { 
    var body: some View {
        Image("apolloimage")
            .centerCropped()
            .edgesIgnoringSafeArea(.all)
    }
}

通过缩放图像以显示图像的完整高度或完整宽度并裁剪在其他维度上悬垂的部分,可以很好地显示图像的中心。


演示:

这是一个演示,展示了图像如何随着图像的增长而居中和裁剪。 在此演示中,框架宽度是恒定的

360
,而框架高度随着滑块向右移动而从
50
700
变化。 开始时,当帧较短时,图像的顶部和底部会被裁剪。 由于帧超出了原始图像的纵横比,因此生成的图像居中,但左右两侧被裁剪。

struct ContentView: View {
    
    @State private var frameheight: CGFloat = 50
    
    var body: some View {
        VStack(spacing: 20) {
            Spacer()
            Image("apolloimage")
                .resizable()
                .scaledToFill()
                .frame(width: 360, height: self.frameheight)
                .clipped()
            Spacer()
            Slider(value: self.$frameheight, in: 50...700)
                .padding(.horizontal, 20)
        }
    }
}

或使用

.centerCropped()
:

进行等效测试
struct ContentView: View {
    
    @State private var frameheight: CGFloat = 50
    
    var body: some View {
        VStack(spacing: 20) {
            Spacer()
            Image("apolloimage")
                .centerCropped()
                .frame(width: 360, height: self.frameheight)
            Spacer()
            Slider(value: self.$frameheight, in: 50...700)
                .padding(.horizontal, 20)
        }
    }
}

Demo running on the simulator of .centerCropped scaling


替代解决方案

制作中心裁剪图像的另一种方法是使图像成为

.overlay()
Color.clear
。 这允许
Color.clear
建立剪切范围。

Color.clear
.overlay(
    Image("apolloimage")
    .resizable()
    .scaledToFill()
)
.clipped()
.contentShape(Rectangle())

对应的

extension
Image
看起来像这样:

extension Image {
    func centerCropped() -> some View {
        Color.clear
        .overlay(
            self
            .resizable()
            .scaledToFill()
        )
        .clipped()
        .contentShape(Rectangle())
    }
}

7
投票

我能够裁剪图像的方形中心以便像 iPhone 照片应用程序一样查看。

extension Image {
    func centerSquareCropped() -> some View {
        GeometryReader { geo in
            let length = geo.size.width > geo.size.height ? geo.size.height : geo.size.width
            self
                .resizable()
                .scaledToFill()
                .frame(width: length, height: length, alignment: .center)
                .clipped()
        }
    }
 }

1
投票

我最初使用@vacawama的答案,它使用

GeometryReader
,但发现实际上这是没有必要的。

(我是用 Xcode 13 编写的,并在 iOS15 中运行,如果这有什么区别的话?)

用这个就足够了...

Image(uiImage: image) // insert your own image here :D
  .resizable()
  .aspectRatio(contentMode: .fill)
  .clipped()

我使用它作为

label
Button
List
参数,所以整个事情就像......

Section("Photo") {
  Button {
    // the action
  } label: {
    if let image = viewStore.imagePickerState.image {
      Image(uiImage: image)
        .resizable()
        .aspectRatio(contentMode: .fill)
        .clipped()
    } else {
      PersonAvatarButton()
    }
  }
}
.aspectRatio(1, contentMode: .fill)
.listRowBackground(Color.gray)
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))

我没有为此在任何地方定义框架,只是

Section
的纵横比。

我最终得到的是一个带有圆角的方形按钮,照片一直到边缘。调整大小以填充但不以任何方式挤压。并且按钮的大小由屏幕尺寸决定。

所以我的代码中没有任何具体尺寸。

© www.soinside.com 2019 - 2024. All rights reserved.