Reputation: 5220
I am trying to align a dynamic collection (ie of varying count) of views to the right of a centered view, without moving the center view from its original, center position.
For example, the center view is Text("12")
and the views to the right are [Text("+3"), Text("+10"), Text("-1")]
so I want the views to the right of 12
be displayed to its right, while 12
is centered horizontally on the screen:
| 12 +3 +10 -1 |
If I try using an HStack
the 12
will move away from center. Is there a view modifier I can use? ex. Text("12").alignToRight(...)
This solution aligns the collection to the left of the right edge of the screen, but that's not the same thing.
ZStack {
Text("12")
HStack(spacing: 4) {
Spacer()
Text("+3")
Text("+10")
Text("-1")
}
}
I would prefer not to have to duplicate the HStack
on the left side and then make it disappear so as to balance the HStack
on the right (that is a ridiculous way of creating a layout, plus the example I'm providing here is simple, in actuality I have to use a ForEach
over the dynamic collection), ie
HStack {
Spacer()
HStack(spacing: 4) {
Spacer()
Text("+3")
Text("+10")
Text("-1")
}
.opacity(0)
Text("12")
HStack(spacing: 4) {
Spacer()
Text("+3")
Text("+10")
Text("-1")
}
Spacer()
}
Using something like Text("12").overlay(myCollectionView.offset(x: 16)
is also winging it, as the font size of the center text will vary, and so I'd have to guess and adjust the offset manually as well -- I'd rather have a padding between the two views.
Upvotes: 2
Views: 7372
Reputation: 257503
Here is possible approach based on alignment guides.
Update: Xcode 14 / iOS 16 - still valid
Tested with Xcode 11.3 / iOS 13.3.
Define custom alignment
extension HorizontalAlignment {
private enum HCenterAlignment: AlignmentID {
static func defaultValue(in dimensions: ViewDimensions) -> CGFloat {
return dimensions[HorizontalAlignment.center]
}
}
static let hCenterred = HorizontalAlignment(HCenterAlignment.self)
}
align needed element of internal HStack
to external VStack
using custom alignment guide
struct TestRightCenterred: View {
var body: some View {
VStack(alignment: .hCenterred) {
HStack(spacing: 4) {
Text("12").border(Color.red) // << just for test
.alignmentGuide(.hCenterred, computeValue: { $0.width / 2.0 })
Text("+3")
Text("+10")
Text("-1")
}
}
.frame(maxWidth: .infinity, alignment: Alignment(horizontal: .hCenterred, vertical: .center))
}
}
Upvotes: 4
Reputation: 21
I was struggling with same problem and come with this solution
struct ContentView: View {
var body: some View {
HStack(spacing: 4) {
HStack { //HStack1
Spacer()
Spacer()
}
Text("12")
HStack(spacing: 4) { //HStack2
Text("+3")
Text("+10")
Text("-1")
Spacer()
}
}
}
}
This code cause "HStack1" and "HStack2" have same width, I got this understanding from this answer
Two Spacers in Hstack1 are needed because one is ignored without other content (can be changed on empty Text("") for example, but I prefer Spacer)
Upvotes: 2