How do you center text here ?
Texts are at the left side of scrollViews.
What is .center for ?
var scrolls = ["One", "Two", "Three"]
VStack {
Spacer()
ForEach(0..<scrolls.count
, id: \.self) { index in
ScrollView(.horizontal, showsIndicators: true) {
Text(" \(scrolls[index])")
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .center)
.background(Color.gray)
}
.frame(maxWidth: .infinity,
maxHeight:50,
alignment:
.center)
.background(Color.green)
}.frame(alignment: .bottom)
}
}
You can use GeometryReader and frame(minWidth: for that, like below.
Tested with Xcode 13.2 / iOS 15.2
var body: some View {
GeometryReader { gr in
VStack {
Spacer()
ForEach(0..<scrolls.count
, id: \.self) { index in
ScrollView(.horizontal, showsIndicators: true) {
Text(" \(scrolls[index])")
.frame(minWidth: gr.size.width, // << here !!
maxHeight: .infinity,
alignment: .center)
.background(Color.gray)
}
.frame(maxWidth: .infinity,
maxHeight:50,
alignment:
.center)
.background(Color.green)
}.frame(alignment: .bottom)
}
}
}
Related
So I have been struggling on this for some time now, so I wanted to reach out and see if someone might be able to assist me.
So I have the following code:
HStack(alignment: .top, spacing: 3) {
VStack(alignment: .leading) {
Text("Location")
.font(.callout.weight(.semibold))
.lineLimit(1)
if item.address != "" {
Group {
Text(item.address)
Text(item.city + ", " + item.state)
}
.font(.footnote)
.foregroundColor(.secondary)
} else {
Text("N/A")
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
VStack {
HStack(alignment: .top, spacing: 3) {
VStack(alignment: .leading) {
Text("Weather")
.font(.subheadline.weight(.semibold))
.lineLimit(1)
HStack(spacing: 3) {
Image(systemName: "waveform")
.imageScale(.small)
.foregroundColor(.secondary)
Text("79%")
.font(.caption.weight(.regular))
.foregroundColor(.secondary)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
VStack(alignment: .leading) {
Text("Reviews")
.font(.subheadline.weight(.semibold))
.lineLimit(1)
HStack(spacing: 3) {
Image(systemName: "star")
.imageScale(.small)
.foregroundColor(.secondary)
Text("4.7 (1212)")
.font(.caption.weight(.regular))
.foregroundColor(.secondary)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.border(.red)
}
Which outputs two main 50%/50% VStack columns:
What is the best way to have the two main VStack use 30%/70%?
I would like to have the first VStack be 30% of the screen view, then the second VStack be 70% of the screen view.
You could use GeometryReader to get the width of the main HStack and set the width of the child VStacks to a percentage of the width which can be accessed through GeometryReader
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
HStack(alignment: .top, spacing: 3) {
firstVStack
.frame(width: geometry.size.width * 0.3)
secondVStack
.frame(width: geometry.size.width * 0.7)
}
}
}
private var firstVStack: some View {
VStack(alignment: .leading) {
Text("Location")
.font(.callout.weight(.semibold))
.lineLimit(1)
if item.address != "" {
Group {
Text(item.address)
Text(item.city + ", " + item.state)
}
.font(.footnote)
.foregroundColor(.secondary)
} else {
Text("N/A")
}
}
}
private var secondVStack: some View {
VStack {
HStack(alignment: .top, spacing: 3) {
VStack(alignment: .leading) {
Text("Weather")
.font(.subheadline.weight(.semibold))
.lineLimit(1)
HStack(spacing: 3) {
Image(systemName: "waveform")
.imageScale(.small)
.foregroundColor(.secondary)
Text("79%")
.font(.caption.weight(.regular))
.foregroundColor(.secondary)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
VStack(alignment: .leading) {
Text("Reviews")
.font(.subheadline.weight(.semibold))
.lineLimit(1)
HStack(spacing: 3) {
Image(systemName: "star")
.imageScale(.small)
.foregroundColor(.secondary)
Text("4.7 (1212)")
.font(.caption.weight(.regular))
.foregroundColor(.secondary)
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
}
}
I am trying to use the PageTabview option to allow a user to move through a series of pages whose data is coming from a JSON file. I want to be able to limit the number of visible dots to 5 or 6 even if there are many values in the field. What I don't want is to have 25 dots if there are twenty-five values in the field. How would I do that? I want to be able to show indicator like an arrow that tells the user there is more to come...Thank you.
My code is below:
struct TopicsExperienceCards: View {
#Binding var closeExperience: Bool
let etype: EItype
var body: some View {
//start of content of zstack layout
ZStack {
VStack(spacing: 20) {
HStack{
Rectangle()
.fill(Color.white)
.frame(width: 300, height: 1, alignment: .center)
Spacer()
Button(action: {
closeExperience = false })
{
Image(systemName:"xmark")
.foregroundColor(Color(etype.accentcolor))
.padding()
}
} //HSTACK
TabView {
ForEach(etype.experience,id: \.self) { item in
// Display the content of a card //
VStack (alignment:.center, spacing:0){
Text(item)
.padding()
.frame(width:300, height:300, alignment:.center)
Divider()
Spacer()
Text("Room for an image")
Spacer()
Spacer()
}//VSTACK
//End of display of content of the card //
} //: FOREACH
} //: TABVIEW
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.onAppear {
setupAppearance() }
} //VSTACK
} //: ZStack
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 16)
.strokeBorder()
.foregroundColor(Color(etype.accentcolor)))
.cornerRadius(16.0)
.padding()
}
}
struct TopicsExperienceCards_Previews: PreviewProvider {
static let etypes: [EItype] = Bundle.main.decode("eibasestructure.json")
static var previews: some View {
TopicsExperienceCards(closeExperience:.constant(true),etype:etypes[1])
}
}
enter image description here
System dots view is limited to around 10 dots, maybe depending on the device. You can't change this value.
Instead of that you can hide system one, and create your own view with dots. As an example you can follow this article, so at the end you'll have something like this:
#State var currentIndex = 0
var body: some View {
//start of content of zstack layout
ZStack {
printUI(currentIndex)
VStack(spacing: 20) {
HStack{
Rectangle()
.fill(Color.white)
.frame(width: 300, height: 1, alignment: .center)
Spacer()
Button(action: {
closeExperience = false })
{
Image(systemName:"xmark")
.foregroundColor(Color.red)
.padding()
}
} //HSTACK
TabView(selection: $currentIndex.animation()) {
ForEach(etype.experience.indices,id: \.self) { i in
let item = etype.experience[i]
// Display the content of a card //
VStack (alignment:.center, spacing:0){
Text(item)
.padding()
.frame(width:300, height:300, alignment:.center)
Divider()
Spacer()
Text("Room for an image")
Spacer()
Spacer()
}//VSTACK
//End of display of content of the card //
} //: FOREACH
} //: TABVIEW
.tabViewStyle(PageTabViewStyle())
.onAppear {
setupAppearance()
}
Fancy3DotsIndexView(numberOfPages: etype.experience.count, currentIndex: currentIndex)
.padding()
.background(Color.green)
.clipShape(Capsule())
} //VSTACK
} //: ZStack
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 16)
.strokeBorder()
.foregroundColor(Color.red)
)
.cornerRadius(16.0)
.padding()
}
Result:
I have a TabView with 7 pages. Each one of the pages has 100 points less than the screen's width.
struct ContentView: View {
var body: some View {
GeometryReader { reader in
VStack(alignment: .center) {
TabView {
ForEach(0..<7) { index in
VStack {
}
.frame( maxHeight: .infinity)
.frame(width: reader.size.width - 100)
.background(Color.red)
.cornerRadius(15)
}
}
.background(Color.yellow)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.frame(width: reader.size.width, height: 500)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
TabView has as much as much width as the screen's width.
There are 50 points on both sides the Vstack's though each one of them has 100 points less than the screen width.
I need to remove the space between the red views.
how about something different using a "ScrollView" and a "HStack", like this:
struct ContentView: View {
var body: some View {
GeometryReader { reader in
ScrollView (.horizontal) {
HStack (spacing: 0) {
ForEach(0..<7) { index in
VStack {
Text("\(index)")
}
.frame(maxHeight: .infinity)
.frame(minWidth: reader.size.width - 100)
.background(Color.red)
.cornerRadius(20)
}
}.frame(maxWidth: .infinity, maxHeight: 500)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
EDIT2: using paging
struct ContentView: View {
var body: some View {
GeometryReader { reader in
VStack(alignment: .center) {
TabView {
ForEach(0..<7) { index in
VStack {
Text("\(index)")
}
.frame(maxHeight: .infinity)
.frame(width: reader.size.width)
.background(Color.red)
.cornerRadius(25)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.frame(width: reader.size.width, height: 500)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
I need to create a table so that the text in the cell expands to the desired size.
I create Content View:
struct ContentView: View {
let items: [String] = [
"1 One Line",
"1 One Line",
"1 One Line"
]
var body: some View {
ScrollView {
VStack(spacing: 1) {
ForEach(self.items, id: \.hash) { (item) in
HStack(spacing: 1) {
Text(item)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.white)
Text("One Line")
.frame(width: 70)
.background(Color.white)
Text("One Line")
.frame(width: 70)
.background(Color.white)
}
}
// MARK: Total
HStack(spacing: 1) {
Text("Title")
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.white)
Text("One Line")
.frame(width: 141)
.background(Color.white)
}
}
.padding(1)
.background(Color.orange)
}
.padding(15)
}
}
and it works well:
But if the text becomes multi-line, then the table breaks:
let items: [String] = [
"1 One Line",
"2 Two Line\nTwo Line",
"3 Three lines\nThree lines\nThree lines"
]
borders are getting wider:
if you make maxHeight = infinity then this solves the problem with borders, but the lines become equal to the maximum height.
The possible solution is to use maxHeight: .infinity with .fixedSize
var body: some View {
ScrollView {
VStack(spacing: 1) {
ForEach(self.items, id: \.hash) { (item) in
HStack(spacing: 1) {
Text(item)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.white)
Text("One Line")
.frame(maxHeight: .infinity, alignment: .center)
.background(Color.white)
Text("One Line")
.frame(maxHeight: .infinity, alignment: .center)
.background(Color.white)
}
.fixedSize(horizontal: false, vertical: true) //<---Here
}
// MARK: Total
HStack(spacing: 1) {
Text("Title")
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.white)
Text("One Line")
.frame(width: 137)
.background(Color.white)
}
}
.padding(1)
.background(Color.orange)
}
.padding(15)
}
I have a vertical ScrollView with a ForEach loop that has HStack's inside with Text, Image, and Two Text's. I'd like to keep each row's items in alignment with each other. I've attempted to wrap in a VStack with alignment .leading but did nothing. The issue I'm having is the text doesn't line up. Still very new to SwiftUI so any help would be appreciated.
ScrollView(.vertical) {
ForEach(self.daily.forecast, id: \.date) { forecast in
HStack (spacing : 10){
Text(forecast.date ?? "")
.fontWeight(.bold)
Spacer()
RequestImage(Url("IMAGE_URL"))
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.clipped()
Spacer()
Text("\(forecast.maxTemp)")
.fontWeight(.bold)
Text("\(forecast.minTemp)")
.fontWeight(.bold)
}
}
}
.padding(.all, 15)
.onAppear() {
authorize {_ in
loadForecast()
}
}
UPDATED:
I was able to get a bit closer but things still aren't aligned to where I'd like them to be.
ScrollView(.vertical) {
ForEach(self.daily.forecast, id: \.date) { forecast in
HStack (spacing : 10){
Text(date)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 45)
Spacer()
RequestImage(Url("IMAGE_URL"))
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.frame(minWidth: 50)
.clipped()
Spacer()
Text(maxTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 45)
Text(minTemp)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color.secondary)
.frame(minWidth: 45)
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
.padding(.all, 15)
.onAppear() {
authorize {_ in
loadForecast()
}
}
Here is a demo of possible solution. Prepared with Xcode 12.4 / iOS 14.4
struct DemoWeatherLayout: View {
private var dayOfTheWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sut"]
var body: some View {
ScrollView {
VStack {
ForEach(dayOfTheWeek, id: \.self) {
WeatherRowView(day: $0,
icon: ["sun.max", "cloud.sun", "cloud", "cloud.snow"].randomElement()!,
low: Array(-5..<0).randomElement()!,
high: Array(1..<15).randomElement()!)
}
}.padding()
}
}
}
struct WeatherRowView: View {
var day: String
var icon: String
var low: Int
var high: Int
var body: some View {
HStack {
Text(day)
.frame(maxWidth: .infinity, alignment: .leading)
Image(systemName: icon)
.frame(maxWidth: .infinity, alignment: .leading)
HStack {
Text("+XXº").foregroundColor(.clear)
.overlay(Text(String(format: "%+d", low)), alignment: .leading)
Text("+XXº").foregroundColor(.clear)
.overlay(Text(String(format: "%+d", high)), alignment: .leading)
}
}
}
}
Three suggestions:
add alignment parameter to HStack:
HStack(alignment: .lastTextBaseline, spacing: 10) { ... //others options: .top, .center, .firstTextBaseline...
Add minLength parameter to Spacer:
Spacer(minLength: 0) but with spacing: 10 in HStack it is a bit redundant. You can remove spacing: 10 for Spacer(minLength: 10)
Add a Spacer(minLength: 0) at the end after Text("\(forecast.minTemp)").fontWeight(.bold)
I'm going to accept #Asperi answer as correct. The UI has indeed changed since to better fit said labels etc. Appreciate everyone's else. The code is very much ugly but works. Not sure if it's 100% correct.
HStack (spacing: 10){
Text(date)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Spacer()
RequestImage(Url("IMAGE_URL_HERE"))
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.frame(minWidth: 55)
.clipped()
Spacer()
Text(maxTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 50)
.background(Color.orange)
.cornerRadius(5)
Text(minTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 50)
.background(Color.blue)
.cornerRadius(5)
}