我想要一个视图,显示使用 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)
}
}
}
}
因此,如果有人能提出任何想法,我将不胜感激。
从我的角度来看,实现此目的最简单的方法是将 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()
,以避免过多。
如果您要固定卡片视图的高度和宽度,那么有一个简单的解决方案:您只需要用
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