SwiftUI 堆栈中元素之间的 maxWidth 不同

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

我想要一个视图,显示使用 customShape 创建的两个元素(它与 RoundedRectangle(cornerRadius:25.0) 的形状基本相同,但与普通矩形组合以使 2 个角锐化)。问题是,当我尝试组合这两个视图时(将其模仿为 Capsule() ),我无法修改其比例。我希望图像仅占据药丸的 25%,而文本占据 75% 的持久部分.但我无法做到这一点。这是我所拥有的照片:

我想要的:

我尝试使用 GeometryReader,但我不知道它是否是一个好的解决方案,或者我是否不知道如何使用它,但我得到了这个:

主视图的代码为:

struct CardHomeContent: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: /*@START_MENU_TOKEN@*/25.0/*@END_MENU_TOKEN@*/)
            HStack(spacing:0) { //Contenido
                ImageContent(image:"Croissant")
                TextCardContent()
            }
        }
        .clipShape(RoundedRectangle(cornerRadius: 25))
        .padding(.horizontal)
    }
}

ImageView 是:

struct ImageContent: View {
    let image : String
    
    var body: some View {
        ZStack {
            Image(image)
                .resizable()
                .scaledToFill()
        }
    }
}

还有文本视图:

struct TextCardContent: View {
    let uiFont: UIFont = .systemFont(ofSize: 13)
    let description = "Unos croissants que harán chuparte los dedos llenos de mantequilla"
    var justifiedTest: String {
        description.justified(font: uiFont, maxWidth: 165)
    }
    var body: some View {
        ZStack {
            RoundedRectangleCustomShapeRight().foregroundStyle(Color.white)
            HStack {
                VStack {
                    Text("Croissants tradicionales").bold().lineLimit(2).minimumScaleFactor(0.4)
                    Text(description).font(.footnote).foregroundStyle(Color.gray).lineLimit(3).minimumScaleFactor(0.5)
                        .multilineTextAlignment(.leading)
                }
                .padding(.all)
                Text(">")
                    .padding(.trailing)
            }

        }
    }
}

因此,如果有人能提出任何想法,我将不胜感激。

ios swift xcode user-interface swiftui
2个回答
0
投票

从我的角度来看,实现此目的最简单的方法是将 CardHomeContent 包装到

GeometryReader
中,然后计算每个卡片视图部分的宽度。即,ImageContent 占总宽度的 0.25,TextCardContent 占总宽度的 0.75。

var body: some View {
   ZStack {
        RoundedRectangle(cornerRadius: 25)
        GeometryReader { geo in //<- Here
            HStack(spacing: 0) {
                ImageContent(image: "Croissant")
                    .frame(width: geo.size.width * 0.25) //<- Here

                TextCardContent()
                    .frame(width: geo.size.width * 0.75) //<- Here
            }
        }
    }
    .clipShape(RoundedRectangle(cornerRadius: 25))
    .padding(.horizontal)
}

您还可以在

ImageContent
中删除ZStack,因为它只是一个图像,并且它的父级已经是一个ZStack。不过,ImageContent 需要更改为
scaledToFit
,或者您可以将
scaledToFill
保留为
clipped()
,以避免过多。


0
投票

如果您要固定卡片视图的高度和宽度,那么有一个简单的解决方案:您只需要用

GeometryReader
包裹形状,如另一个答案中所述。

但是,如果你想让卡片采用自然高度,那就有点棘手了。这里有两个技巧:

1。使用带有覆盖层的隐藏足迹

  • 足迹(特别是高度)是使用隐藏内容建立的。文本部分可能可以用于此目的。
  • 理想情况下,应使用虚拟文本(可能包含换行符)来查找足迹,以便所有实例的足迹高度始终相同。
  • 覆盖层包含一个
    GeometryReader
    ,用于测量足迹的大小。
  • 带有圆角的剪辑形状可以应用于整体构图。这样,就不需要在文本部分应用半圆角矩形了。
struct CardHomeContent: View {
    var body: some View {
        TextCardContent()
            .frame(maxWidth: .infinity)
            .hidden()
            .overlay {
                GeometryReader { proxy in
                    HStack(spacing:0) { //Contenido
                        ImageContent(image: "Croissant")
                            .frame(width: proxy.size.width / 4)
                            .frame(maxHeight: .infinity)
                            .clipped()
                        TextCardContent()
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .background(.white)
                    }
                }
            }
            .clipShape(RoundedRectangle(cornerRadius: 25))
    }
}

struct ImageContent: View {
    // as betore
}

struct TextCardContent: View {
    let description = "Unos croissants que harán chuparte los dedos llenos de mantequilla"

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text("Croissants tradicionales")
                    .bold()
                    .lineLimit(2)
                    .minimumScaleFactor(0.4)
                Text(description)
                    .font(.footnote)
                    .foregroundStyle(Color.gray)
                    .lineLimit(3)
                    .minimumScaleFactor(0.5)
                    .multilineTextAlignment(.leading)
            }
            .padding()
            Text(">")
                .padding(.trailing)
        }
    }
}

默认情况下,

CardHomeContent
将获取尽可能多的宽度,因为足迹使用
maxWidth: .infinity
。可以通过将
frame
应用于
CardHomeContent
来限制宽度:

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.gray
            CardHomeContent()
                .frame(width: 350)
        }
    }
}

2。使用自定义布局

解决问题的另一种方法是使用自定义

Layout
。在以下示例实现中,布局仅期望有两个子视图,并且整个卡片的高度由第二个子视图决定:

struct CardLayout: Layout {
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let w = proposal.width ?? 100
        var h = CGFloat.zero
        if subviews.count == 2, let lastSubview = subviews.last {
            let subviewSize = lastSubview.sizeThatFits(.init(width: w * 3 / 4, height: proposal.height))
            h = subviewSize.height
        }
        return CGSize(width: w, height: h)
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        if let w = proposal.width, subviews.count == 2,
           let firstSubview = subviews.first, let lastSubview = subviews.last {
            let subviewSize = lastSubview.sizeThatFits(.init(width: w * 3 / 4, height: proposal.height))
            let h = subviewSize.height
            firstSubview.place(
                at: bounds.origin,
                proposal: .init(width: w / 4, height: h)
            )
            lastSubview.place(
                at: CGPoint(x: bounds.minX + bounds.width / 4, y: bounds.minY),
                proposal: .init(width: w * 3 / 4, height: h)
            )
        }
    }
}

struct CardHomeContent: View {
    var body: some View {
        CardLayout { //Contenido
            ImageContent(image: "Croissant")
                .clipped()
            TextCardContent()
                .background(.white)
        }
        .clipShape(RoundedRectangle(cornerRadius: 25))
    }
}

// ImageContent, TextCardContent and ContentView as before

Screenshot

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