如何在 SwiftUI 中居中裁剪图像

Posted

技术标签:

【中文标题】如何在 SwiftUI 中居中裁剪图像【英文标题】:How to center crop an image in SwiftUI 【发布时间】:2020-12-18 09:48:21 【问题描述】:

我是 SwiftUI 的新手。我想每个人都在这一点上。我已经做了大约 6 年的应用程序开发人员,我觉得在 *** 上问这个问题很愚蠢。但我到处找。如何在 SwiftUI 的 ImageView 中居中裁剪图像?

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

【问题讨论】:

【参考方案1】:

Android 的 ImageView.ScaleType documentation 将 CENTER_CROP 描述为:

CENTER_CROP

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

这基本上是 Aspect Fill Scaling(又名.scaledToFill())所做的,除了(令人惊讶的是)Aspect Fill 不会裁剪掉帧之外的部分.

通过制作图像.resizable,并应用.scaledToFill()。图像将按比例缩放以填充其可用框架,并根据需要保留顶部和底部或侧面。 .clipped() 然后删除框架外的图像部分。

Image("myImage")
    .resizable()
    .scaledToFill()
    .frame(width: 200, height: 200, alignment: .center)
    .clipped()

为了更方便,我创建了这个extension of Image

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

要使用Imageextension,只需将其放入项目中的文件中(image-centercropped.swift 之类的名称就可以很好地工作)。然后只需将.centerCropped() 添加到您想要居中裁剪的任何图像。

Image("apolloimage").centerCropped()

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

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

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

将通过缩放图像以显示图像的全高或全宽并在另一个维度上裁剪悬垂部分来很好地显示图像的中心。


演示:

这是一个演示,它显示了图像如何随着图像的增长而居中和裁剪。在此演示中,帧宽度为常数360,而帧高度随着滑块向右移动而从50 变化到700。在帧短的开始,图像的顶部和底部被裁剪。由于帧超过了原始图像的 aspectRatio,因此生成的图像居中但在左右两侧被裁剪。

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)
        
    


替代解决方案

制作中心裁剪图像的另一种方法是使图像成为.overlay()Color.clear。这允许Color.clear 建立剪辑边界。

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

而对应的extensionImage 看起来像这样:

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

【讨论】:

@vacawama 如果您不使用框架,我会将此标记为正确答案。我不想添加特定的框架值(高度或宽度)。我希望图像根据屏幕尺寸自行调整大小。 再看一遍。使用我提供的Imageextension。您不必指定框架。只需使用Image("imageName").centerCropped()。我什至向你展示了如何让它填满手机的屏幕,而不需要指定框架。 要使用Image extension,只需将其放入项目的文件中即可。称之为“image-centercropped.swift”。然后只需将.centerCropped() 添加到您想要居中裁剪的任何图像。无需指定框架。【参考方案2】:

我能够裁剪图像的方形中心,以便像 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()
        
    
 

【讨论】:

【参考方案3】:

我最初使用@vacawama 的答案,它使用GeometryReader,但发现实际上这不是必需的。

(我使用 Xcode 13 编写并在 ios15 中运行,如果这有什么不同?)

使用这个就足够了...

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

我将其用作ButtonListlabel 参数,所以整个事情就像...

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 的纵横比。

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

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

【讨论】:

以上是关于如何在 SwiftUI 中居中裁剪图像的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SwiftUI 实现已裁剪为形状的缓慢滑动图像?

如何在 ffmpeg 中居中裁剪视频缩略图(方形缩略图)?

SwiftUI - 缩放、缩放和裁剪图像

如何在 QWidget 中居中背景图像

如何在 div 中居中(背景)图像?

如何在引导列中居中图像[重复]