I've got a list of items that display a title, a subheading and their distance. The subheading and title can both change in size and may be smaller or large depending on data. The problem is I want the Text(distance) to remain in the same position regardless of the heading or subheading size.
var body: some View {
VStack {
Divider()
HStack {
Circle()
.fill(index < 7 ? .blue : .white)
.frame(width: 10, height: 10)
.padding(.leading, 4)
Circle()
.fill(getColor(index: index))
.scaledToFit()
.frame(height: 35)
.clipShape(Circle())
HStack {
VStack (alignment: .leading) {
Text(title)
.fontWeight(.medium)
.minimumScaleFactor(20)
.foregroundColor(.black)
Text(subheading)
.font(.subheadline)
.lineLimit(2)
.foregroundColor(.secondary)
.multilineTextAlignment(.leading)
}
}
Text("\(distance)")
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
Spacer()
}.frame(maxWidth: .infinity)
}
}
I've got some circles in there too that are based on the design, but they can be ignored. My issue is towards the bottom of the HStack. Trying to add padding etc. hasn't worked and causes strange renders of the view.
I would have added this as a comment, however, I am not sure what you mean by keeping the Text("\(distance)") the same position.
You may be able to use a ZSTack and then set the .offset of the Text field, but this would mean that the distance text may overlap the other text
Related
I am creating a "News" SwiftUI widget and I need to display rows, each containing time and news header.
I am using a VStack containing HStack's for each item but I am encountering an issue with extra HStack spacing between the two Text's. I need the left Text taking all the width needed for its content and the right Text taking all the width that is left. Seems to work as expected for the longer date (second line) for not for the first one.
In AutoLayout world I would need to play around with Content hugging / Compression resistance priority to achieve what I need but not sure what to do in SwiftUI.
Here's my code for creating the view:
private func createView(from headers: [NewsHeaderEntry.NewsHeader], with first: Int) -> some View {
GeometryReader { metrics in
VStack(alignment: .leading, spacing: 16) {
title
ForEach(headers[0...first - 1]) { header in
HStack(spacing: 4) {
Text(header.newsAt)
.font(.system(size: 12))
.fontWeight(.light)
.background(.green)
.frame(alignment: .leading)
Text(header.title)
.font(.system(size: 14))
.fontWeight(.semibold)
.redacted(reason: header.isPlaceholder ? .placeholder : .init())
.background(.yellow)
.lineLimit(2)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.background(.red)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal, 8)
}
Spacer()
}
.frame(width: metrics.size.width)
.background(.blue)
}
}
I have also tried setting maxWidth: .infinity for the left Text as well but it resulted in equal widths for both Texts which is not what I need.
Update: Without .padding(.horizontal, 8) the spacing works as expected but I need the horizontal spacing from the design perspective.
[2:
What you're looking for is the default behavior of the stack. Your frame modifiers are the issue:
private func createView(from headers: [NewsHeaderEntry.NewsHeader], with first: Int) -> some View {
GeometryReader { metrics in
VStack(alignment: .leading, spacing: 16) {
title
ForEach(headers[0...first - 1]) { header in
HStack(spacing: 4) {
Text(header.newsAt)
.font(.system(size: 12))
.fontWeight(.light)
.background(.green)
Text(header.title)
.font(.system(size: 14))
.fontWeight(.semibold)
.redacted(reason: header.isPlaceholder ? .placeholder : .init())
.background(.yellow)
.lineLimit(2)
}
.background(.red)
.padding(.horizontal, 8)
}
Spacer()
}
.frame(width: metrics.size.width)
.background(.blue)
}
}
I'm creating an iOS minesweeper game and want to have a bar at the top with three pieces of information:
The high score (next to a trophy symbol) [LEFT]
The current time (next to a timer symbol) [CENTER]
The number of bombs left (next to a bomb symbol) [RIGHT]
However, the elements are not evenly aligned as you can see in the picture at the bottom- I'm guessing the spacers are automatically the same size. So, because the text on the left side of the screen is wider than that on the right, the middle text is offset to the right.
How can I align these items so the center time/image is perfectly in the middle with the other objects on the left/right?
My code looks like:
struct GameView: View {
#EnvironmentObject var game: Game
#State var labelHeight = CGFloat.zero
var body: some View {
VStack(alignment: .center, spacing: 10) {
Text("MINESWEEPER")
.font(.system(size: 25, weight: .bold, design: .monospaced))
.kerning(3)
.foregroundColor(.red)
HStack(alignment: .center, spacing: 5) {
Image(systemName: "rosette")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.highScoreString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image(systemName: "timer")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.timerString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image("top_bomb")
.resizable()
.aspectRatio(contentMode: .fit)
Text(String(game.bombsRemaining))
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
.overlay(GeometryReader(content: { geometry in
Color.clear
.onAppear(perform: {self.labelHeight = geometry.frame(in: .local).size.height})
}))
}
.frame(width: UIScreen.main.bounds.width * 0.9, height: labelHeight, alignment: .center)
BoardView()
}
}
}
Instead of aligning by spacers, move each part into separate view, say LeftHeader, CenterHeader, and RightHeader and use frame alignment, which gives exact separation by equal sizes, like
HStack {
LeftHeader()
.frame(maxWidth: .infinity, alignment: .leading)
CenterHeader()
.frame(maxWidth: .infinity, alignment: .center)
RightHeader()
.frame(maxWidth: .infinity, alignment: .trailing)
}
This can be achieved using a ZStack , HStack and Spacers.
ZStack{
HStack{
Text("Left")
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right")
}
}
Even if you remove left ( or right ) HStack, it won't affect the other components or the layout.
You can make it look bit nicer by adding paddings like this,
ZStack{
HStack{
Text("Left").padding([.leading])
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right").padding([.trailing])
}
}
I think you can create more one stack.
My solution like:
/**
* spacing is an example
* minWidth calculated by fontSize
*/
HStack(alignment: .center, spacing: 20){
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
}
Hope it useful for you.
I'm having a bit of trouble with this particular view. For some reason, it seems like depending on whether the Text(title) element is one line or two lines, I'm getting very odd behavior for the total height of the view. All the elements are dynamically populated so I can't just have a set height. I've ruled out that this is issue not dependent on the image that's loaded, but always occurs when the Text(title) element is one line. As you can see, when the element is only one line, it causes the image to have a padding above it and the 'See more' button to have a padding below it. The first image shows the margin issue (single line for title) and the second shows how the margins aren't there for multi-line title. Any help much appreciated!
https://i.stack.imgur.com/MB6Dt.jpg
https://i.stack.imgur.com/RDKOj.jpg
VStack(alignment: .leading) {
Image(systemName: "camera")
.data(url: URL(string: picture)!)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width * 0.8)
.maxHeight(215)
.clipped()
Group {
HStack {
Text(title)
.bold()
.font(.system(size: 20, weight: .bold))
.padding(.vertical, 5)
Spacer()
}
HStack {
Text(subInformation)
.font(.system(size: 17, weight: .semibold))
.foregroundColor(Color.red)
.padding(.bottom, 5)
Spacer()
}
HStack {
Text(description)
.padding(.bottom, 2)
.foregroundColor(Color.gray)
Spacer()
}
}.padding(.horizontal)
DestinationAd()
.frame(width: UIScreen.main.bounds.width * 0.8, height: 50)
.padding(.bottom, 5)
Link(destination: URL(string: urlToOpen)!, label: {
HStack {
Spacer()
Text("See more")
.padding(.vertical, 5)
Spacer()
}.background(Color.blue)
.cornerRadius(10)
}).padding(.horizontal, 10)
.padding(.bottom, 10)
}.frame(width: UIScreen.main.bounds.width * 0.8)
.background(Color.white)
.cornerRadius(20)
I think that everything works as expected in your example. You are populating VStack with some child elements. VStack wraps it's content by default. In the first case (when the text only has one line) child views take less space that in the second example.
If you don't want to set frame for your VStack, maybe Spacer after the image and before the button could help.
I just learned swiftUI and I got little trouble. I want to make navigationBarTitle and title headline alignment like this:
Image 1: I want to make my view like this
I have tried to make like below but it does not work:
struct HeaderView: View {
var body: some View {
NavigationView {
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(width: 370)
Spacer()
}
.navigationBarTitle("Chelsea FC")
}
}
}
From my code above, I got a view like this:
Image 2: I got a view like this from my code above
Could someone help me how to get a view like I want
Try leading alignment
var body: some View {
NavigationView {
VStack(alignment: .leading) { // << here !!
// ... no changes in image
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.padding(.leading) // << here !!
.multilineTextAlignment(.leading)
}
You should add alignment to StackView. You can change alignment to .leading, .trailing or .center. It is centered by default thats why you are having the label in center.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
// Your Code
}
}
}
Remove .frame(width: 370) and use .frame(maxWidth: .infinity) so that the text takes the whole width of its parent.
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
Spacer()
}
I am trying to replicate a calculator app. I cant seem to modify my 'Text' view to look similarly.
Text("0")
.background(Color.gray)
.cornerRadius(10)
.font(.largeTitle)
But its far from what I am trying to replicate.
I tried offset, but it offsets the entire 'Text' view.
Basically I want my Text to look like what is pointed in the image
You can achieve this playing around with ZStack, VStack, HStack and Spacer(). Here is a quick example:
struct CalculatorText: View {
var body: some View {
ZStack {
Rectangle()
.cornerRadius(10)
.foregroundColor(.gray)
VStack {
Spacer() // now the text will be on the bottom of ZStack
HStack {
Spacer() // and now the text will be on the right side of ZStack
Text("0")
.bold()
.font(.system(size: 30))
.foregroundColor(.white)
.multilineTextAlignment(.trailing)
.padding()
}
}
}
.frame(height: 100)
.padding()
}
}
and the result will be: