SwiftUI:如何找到图像的高度并使用它来设置框架的大小

Posted

技术标签:

【中文标题】SwiftUI:如何找到图像的高度并使用它来设置框架的大小【英文标题】:SwiftUI: How to find the height of an image and use it to set the size of a frame 【发布时间】:2020-07-13 14:46:49 【问题描述】:

所以我一直试图弄清楚如何在 SwiftUI 中找到图像的高度,但没有成功。基本上我有纵向/横向图像,我想以大约 600 pt 高显示纵向图像,而我想显示横向图像,因此使用图像的宽度,所以高度是任意的。话虽如此,我希望它在 GeometryReader 中,因为我使用的偏移量使用它,因此当您向下滚动时,图像不会滚动,但如果您向上滚动,它会滚动。我不相信我可以共享图像,但无论如何,这应该足以成功运行它!

struct ContentView: View 
    
    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
  
    var backButton: some View 
        Button(action: 
            withAnimation 
                self.mode.wrappedValue.dismiss()
            
        ) 
            Image(systemName: "chevron.left.circle.fill")
                .opacity(0.8)
                .font(.system(size: 30))
                .foregroundColor(.black)
                .background(Color.gray.mask(Image(systemName: "chevron.left").offset(x: 9.4, y: 7.6)))
                
        
    

    var body: some View 
        ScrollView(showsIndicators: false) 
            
                GeometryReader  geometry in
                    Image("image1")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(maxWidth: UIScreen.main.bounds.width, minHeight: 350, maxHeight: 600, alignment: .top)
                        .clipped()
                        .overlay(
                            Rectangle()
                                .foregroundColor(.clear)
                                .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
                        )
                       .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

                
                .frame(minWidth: UIScreen.main.bounds.width, maxWidth: UIScreen.main.bounds.width, minHeight: 350, idealHeight: 600, maxHeight: 600)

                //Message
                VStack(alignment: .leading) 
                    Text("\(07/22/20) - Day \(1)")
                        .font(.body)
                        .fontWeight(.light)
                        .foregroundColor(Color.gray)
                        .padding(.leading)
                    Text("what I Love)
                        .font(.title)
                        .fontWeight(.bold)
                        .padding([.leading, .bottom])

                    Text("paragraph")
                        .padding([.leading, .bottom, .trailing])
                
   
        
        .edgesIgnoringSafeArea(.all)
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: backButton)
    

任何帮助将不胜感激!

编辑:这里有两张图片供参考 - 抱歉,我没有意识到你能做到,虽然我应该想到这一点!

【问题讨论】:

您能插入一张图片来了解您想要的最终结果吗?否则很难帮到你。 @Gius 对不起,我没有意识到我可以!我还是新手哈哈!但我只是一个纵向和横向的图像! 如果我理解正确,您希望纵向图像以最大 600 的高度显示,而您希望以最大宽度显示横向图像,对吧? 【参考方案1】:

如果我理解正确,您想要图像的大小,以便可以在自己的框架中使用它的尺寸?您可以创建一个函数,接收图像名称并返回您想要的图像,同时为您可以在框架中使用的 widthheight 设置 @State 变量。

    @State var width: CGFloat = 0
    @State var height: CGFloat = 0
    
    func image(_ name: String) -> UIImage 
        let image = UIImage(named: name)!
        let width = image.size.width
        let height = image.size.height

        DispatchQueue.main.async 
            if width > height 
                // Landscape image
                // Use screen width if < than image width
                self.width = width > UIScreen.main.bounds.width ? UIScreen.main.bounds.width : width
                // Scale height
                self.height = self.width/width * height
             else 
                // Portrait
                // Use 600 if image height > 600
                self.height = height > 600 ? 600 : height
                // Scale width
                self.width = self.height/height * width
            
        
        return image
    

用法:

    GeometryReader  geometry in
        Image(uiImage: image("image1"))
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(width: width, height: height)
            .overlay(
                Rectangle()
                    .foregroundColor(.clear)
                    .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
            )
            .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

编辑:添加了 DispatchQueue.main.async 块作为“在视图更新期间修改状态”警告的快速修复。有效,但可能不是绝对最好的方法。将图像放入它自己的@State 并将其设置在视图的onAppear 方法中可能会更好。

编辑 2:将图像移动到它自己的 @State var 中,我认为这是一个更好的解决方案,因为它可以使视图更可重用,因为您可以传递图像而不是硬编码视图中的图像名称。

    @State var image: UIImage 
        didSet 
            let width = image.size.width
            let height = image.size.height
            DispatchQueue.main.async 
                if width > height 
                    // Landscape image
                    self.width = width > UIScreen.main.bounds.width ? UIScreen.main.bounds.width : width
                    self.height = self.width/width * height
                 else 
                    
                    self.height = height > 600 ? 600 : height
                    self.width = self.height/height * width
                
            
        
    

用法:

    GeometryReader  geometry in
        Image(uiImage: image)
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(width: width, height: height)
            .overlay(
                Rectangle()
                    .foregroundColor(.clear)
                    .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
            )
            .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

必须通过传入 UIImage 来更新 ContentView 的声明方式:

ContentView(image: UIImage(named: "image0")!)

【讨论】:

是的,这非常有效!我唯一更改的是语句 else 部分中的 self.width 等于 UiScreen.main.bounds.width 以便它适合我所有的肖像图像宽度!非常感谢!! 虽然经过进一步测试-似乎这在预览之外不起作用,这很奇怪!我遇到了“在视图更新期间修改状态,这将导致未定义的行为”的问题。我的图片没有加载到应用程序上 - 关于如何解决这个问题的任何想法? 哎呀,我应该在 sim 中运行它,对不起!我去看看有什么需要改的。 我更新了答案,所以宽度和高度在主线程上更新。这将修复Modifying state 警告,但可能有更好的方法来解决它。如果我想出更好的解决方案会更新。 很高兴听到。我进行了第二次更新,其中 image 位于它自己的 @State var 中,这使得它更具可重用性,但是当您使用它时,您必须将 UIImage 传递给 ContentView。

以上是关于SwiftUI:如何找到图像的高度并使用它来设置框架的大小的主要内容,如果未能解决你的问题,请参考以下文章

使用 SwiftUi 为 Apple Watch 构建一个基本的高度计

如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮

将图像名称传递给 SwiftUI 中的自定义图像子类

如何在 SwiftUI 中根据其子级高度设置 GeometryReader 高度?

如何在具有(流体高度)的响应框中垂直居中文本[重复]

SwiftUI - 在当前视图中为新图像设置动画