Reputation: 97
I have been trying to achieve to match timeline sidebar height as per the content below header.
But side bar is taking full height, is there any way we can restrict it's height as of middle content. it's in HStackView as below.
//header
HStack {
Circle()
.fill(Color.blue)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Headline").font(.headline)
}.padding(0)
//content
HStack {
VStack {
// Text("l").padding(.leading,5)
// Text("l").padding(.leading,5)
Rectangle().frame(width: 20)
}
Text("Time Line Content Time Line Content Time Line Content Time Line Content fdfgfuysdgfuydsgfgds fgfyusdfyfdsdfsdfgyusdg fydsufidsfy uyfudsfuydsufysdfsdf dfusdtfoisdtftdsoftsdftsydtfsdtfodstfdstf fgdsygfdsgfuygu").font(.caption)
}
//footer
HStack {
Circle()
.fill(Color.orange)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Footer").font(.subheadline)
}.padding(0)
Thanks.
Upvotes: 5
Views: 7179
Reputation: 59
A neat option is to combine an overlay and the GeometryReader:
HStack {
Text("some long text........")
.font(.body)
.padding(.horizontal, 20)
}
.overlay(
GeometryReader { proxy in
Rectangle()
.frame(width: 20, height: proxy.size.height)
}
)
Upvotes: 3
Reputation: 20234
From what I understood of your question is that:
HStack
in which the leftmost view is a Rectangle
and the rightmost view is a Text
.Rectangle
to be the same height as the Text
.The problem is that the height of the HStack
is based on the tallest child view which happens to be the Rectangle
but a Rectangle
view does not have any intrinsic size like Text
and will occupy all the space the parent provides, or if you manually apply a frame.
You set a width of 20 but leave height and so it takes the entire height it can get.
This indicates that we need to set the height of the Rectangle
to be same as the dynamic Text
but the problem is that we don't know the height upfront.
To solve this:
Text
.
GeometryReader
and access the height value.PreferenceKey
Rectangle
when it gets to know the Text
height
@State
variable will suffice nowstruct ContentLengthPreference: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct ContentView: View {
@State var textHeight: CGFloat = 0 // <-- this
var body: some View {
HStack {
Rectangle()
.frame(width: 20, height: textHeight) // <-- this
Text(String(repeating: "lorem ipsum ", count: 25))
.overlay(
GeometryReader { proxy in
Color
.clear
.preference(key: ContentLengthPreference.self,
value: proxy.size.height) // <-- this
}
)
}
.onPreferenceChange(ContentLengthPreference.self) { value in // <-- this
DispatchQueue.main.async {
self.textHeight = value
}
}
}
}
ContentLengthPreference
as our PreferenceKey
implementationText
; Apply overlay
containing GeometryReader
overlay
will have same height as Text
GeometryReader
, Color.clear
is just a filler invisible viewanchorPreference
modifier allows us to access and store heightonPreferenceChange
modifier on parent HStack
can catch the value passed by child viewtextHeight
textHeight
can be applied on Rectangle
and will update the view when this value updatesCredits: https://www.wooji-juice.com/blog/stupid-swiftui-tricks-equal-sizes.html
If you have multiple of these in a List
then you don't need to do anything. Each row will size automatically upto the Text
height.
It's free!!!
struct ContentView: View {
var body: some View {
List(0..<20) { _ in
ArticleView()
}
}
}
struct ArticleView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Circle()
.fill(Color.blue)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Headline").font(.headline)
}
HStack {
Rectangle().frame(width: 20)
Text(String(repeating: "lorem ipsum ", count: (5...50).randomElement()!))
}
HStack {
Circle()
.fill(Color.orange)
.frame(width: 15, height: 15)
.overlay(Circle().inset(by: 2).fill(Color.white))
Text("Footer").font(.subheadline)
}
}
}
}
Upvotes: 6
Reputation: 18904
You can add max-height like this
HStack {
VStack {
// Text("l").padding(.leading,5)
// Text("l").padding(.leading,5)
Rectangle().frame(width: 20)
}
Text("Time Line Content Time Line Content Time Line Content Time Line Content fdfgfuysdgfuydsgfgds fgfyusdfyfdsdfsdfgyusdg fydsufidsfy uyfudsfuydsufysdfsdf dfusdtfoisdtftdsoftsdftsydtfsdtfodstfdstf fgdsygfdsgfuygu").font(.caption)
}.frame(minHeight: 0, maxHeight: .infinity) //<---here
Or Set up your cell view like this
struct ContentCellView: View {
var isLast: Bool = false
var body: some View {
HStack(alignment: .top, spacing: 0) {
VStack(alignment: .leading) {
Image(systemName: "message.circle").frame(width: 30)
if !isLast {
Rectangle().fill(Color.black).frame(width: 1).padding(.leading, 15.5)
}
}
Text("Time Line Content Time Line Content Time Line Content Time Line Content fdfgfuysdgfuydsgfgds fgfyusdfyfdsdfsdfgyusdg fydsufidsfy uyfudsfuydsufysdfsdf dfusdtfoisdtftdsoftsdftsydtfsdtfodstfdstf fgdsygfdsgfuygu")
}.frame(minHeight: 0, maxHeight: .infinity)
}
}
struct ContentViewList: View {
var body: some View {
ScrollView{
VStack(spacing: 0){
ForEach((1...10).reversed(), id: \.self) {
ContentCellView(isLast: $0 == 1) // isLast for not showing last line in last cell
}
}
}.padding()
}
}
Upvotes: 1