How align columns for multiple items on a VStack on SwiftUI? - swiftui

In the sample below, if I have a VStack followed for a HStack, how can I make the width of the HStack follow the same as a grid ?
struct VStackAlignmentView: View {
let data = ["a", "b", "cdefghl alalala", "i", "j"]
var body: some View {
VStack(spacing: 0) {
ForEach (data, id:\.self) { item in
HStack(alignment: .top) {
Text("Column 1")
VStack(alignment: .trailing) {
Text("Column \(item)")
Text("Value 2")
}
.frame(minWidth: 120)
Text("Column 3")
Spacer()
}
}
}
}
}
This is the result I got:
The text on the 3rd row is aligned to the right, but the other rows it adds a space to that
I have tried a hack to for the VStack(alignment: .trailing) by adding an empty Text with the frame width and it works, but I don't think that is an elegant way.
I also tried to use alignmentGuide but it push the views to other way.
Anyone with the same problem?

Using you're code, I believe this is what you're looking for:
struct VStackAlignmentView: View {
let data = ["a", "b", "cdefghl alalala", "i", "j"]
var body: some View {
VStack(alignment: .center, spacing: 20) {
ForEach (data, id:\.self) { item in
HStack(alignment: .top, spacing: 20) {
Text("Column 1")
VStack(alignment: .center) {
Text("Column \(item)")
Text("Value 2")
}
.frame(minWidth: 120)
Text("Column 3")
}
.frame(maxWidth: .infinity)
}
}
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
}
}
But if you're just creating a grid layout, it will be much easer to use a LazyGrid:
struct VStackAlignmentView2: View {
let data = ["a", "b", "cdefghl alalala", "i", "j"]
var body: some View {
LazyVGrid(
columns: [
GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())
],
alignment: .center,
spacing: 10,
pinnedViews: [],
content: {
ForEach (data, id:\.self) { item in
Text(item)
}
})
}
}

Related

SwiftUI Navigation Title Space

Hey guys I had a quick question hopefully you guys are able to help. Im using swiftUI to make this app. When I use NavigationLink to get to different views there's always this extra space that I can't get rid of. I've tried
'.navigationbarIsHidden(true)'
but that just doesn't work at all
code for view 1:
struct MenuView: View{
var column = [GridItem(.adaptive(minimum: 160), spacing: 20)]
#StateObject var cartManager = CartManager()
var body: some View{
NavigationView{
VStack {
ScrollView(.vertical, showsIndicators: false){
LazyVGrid(columns: column, spacing: 20){
ForEach(productList, id: \.id){ product in
ProductCard(product: product)
.environmentObject(cartManager)
}
}.padding()
}
.navigationTitle("Menu")
.navigationBarTitleDisplayMode(.automatic)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(
destination:
CartView()
.environmentObject(cartManager)
){
CartButton(numberOfProducts: cartManager.products.count)
}
}//TOOLBARITEM
}//TOOLBAR
}//VSTACK
}//NAVVIEW
}}
code for view 2:
struct CartView: View {
#EnvironmentObject var cartManager: CartManager
var body: some View {
NavigationView {
VStack {
// Text("Cart")
ScrollView(.vertical, showsIndicators: false){
if(cartManager.products.count > 0){
ForEach(cartManager.products, id: \.id){
product in
ProductRow(product: product)
.environmentObject(cartManager)
}
VStack {
HStack{
Text("Tax:")
.font(.title)
Spacer()
Text("$\(cartManager.tax, specifier: "%.2f")")
.bold()
}.padding()
HStack{
Text("Tip:")
.font(.title)
Spacer()
Text("$\(cartManager.tip, specifier: "%.2f")")
.bold()
}.padding()
HStack{
Text("Total:")
.font(.title)
Spacer()
Text("$\(cartManager.total, specifier: "%.2f")")
.bold()
}
}.padding()
}else{
Spacer(minLength: 300)
Image(systemName: "takeoutbag.and.cup.and.straw.fill")
.font(.largeTitle)
Text("Your cart is empty!")
.font(.title2)
Text("Add things to your order to continue!")
.font(.title2)
}
}
}
.navigationTitle("Your Order")
.navigationBarTitleDisplayMode(.automatic)
}
}
}

SwiftUI: Custom label in navigation link is greyed out

I'm trying to use my custom card view as label to navigation link. this navigation link is also a Grid item.
The result is a
My Card Code:
struct CityCard: View {
var cityData: CityData
var body: some View {
VStack {
Text("\(cityData.name)")
.padding([.top], 10)
Spacer()
Image(systemName: cityData.forecastIcon)
.resizable()
.frame(width: 45, height: 45)
Spacer()
HStack(alignment: .bottom) {
Text(String(format: "%.2f $", cityData.rate))
.padding([.leading, .bottom], 10)
Spacer()
Text(String(format: "%.2f °", cityData.degrees))
.padding([.trailing, .bottom], 10)
}
}.frame(width: 150, height: 150)
.background(Color.blue)
.cornerRadius(10)
.navigationTitle(cityData.name)
}
}
My List View:
struct CityList: View {
var cities: [CityData]
let columns = [
GridItem(.flexible(minimum: 150, maximum: 150)),
GridItem(.flexible(minimum: 150, maximum: 150))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(self.cities) { item in
NavigationLink(destination: Text(item.name), label: {
CityCard(cityData: item)
})
}
}
}
}
}
someone has a solution why it gives me this opacity?
Update:
The main contact view is:
It's greyed out because you don't yet have a NavigationView in your view hierarchy, which makes it possible to actually navigate to a new view.
You can, for example, wrap your ScrollView in a NavigationView:
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(self.cities) { item in
NavigationLink(destination: Text(item.name), label: {
CityCard(cityData: item)
})
}
}
}
}
}
Or, if it's only happening in your preview, add a NavigationView to your preview code:
NavigationView {
CityList(...)
}
Keep in mind that if you have the default accent color (blue, in light mode) that your links will become invisible (since they will be blue) on top of the blue background. You can set a custom accent color in your project or via the accentColor modifier:
NavigationView {
//content
}.accentColor(.black)

ScrollViewReader is not working and building is pretty slow

I couldn't figure out why it is not working my ScrollViewReader below my code. Please help me figure out the issue. I want to pass my zolyric.number into scrollToIndex and that scrollToIndex will set the number that I want to scroll in my HymnLyrics(). Also, although I couldn't find the error, the building is pretty slow.
Here ZolaiTitles() is the same list just sorting the song titles algebraically and HymnLyrics() is the one to display in the canvas.
struct ZolaiTitles: View {
#AppStorage("scrollToIndex") var scrollToIndex: Int?
#EnvironmentObject var tappingSwitches: TapToggle
let zoLyrics: [Lyric] = LyricList.hymnLa.sorted { lhs, rhs in
return lhs.zoTitle < rhs.zoTitle
}
var body: some View {
ScrollView {
ForEach(zoLyrics, id: \.id) { zoLyric in
VStack {
Button(action: {
//if let lyricNum = String(zoLyric) {
scrollToIndex = zoLyric.number // zolyric.number is already an interger.
//}
self.tappingSwitches.isHymnTapped.toggle()
}, label: {
HStack {
Text(zoLyric.zoTitle)
.foregroundColor(Color("bTextColor"))
.lineLimit(1)
.minimumScaleFactor(0.5)
Spacer()
Text("\(zoLyric.number)")
.foregroundColor(Color("bTextColor"))
}
})
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding([.leading, .bottom, .trailing])
}
}
}
}
struct HymnLyrics: View {
#AppStorage("scrollToIndex") var scrollToIndex: Int = 1
var lyrics: [Lyric] = LyricList.hymnLa
#AppStorage("fontSizeIndex") var fontSizeIndex = Int("Medium") ?? 18
#AppStorage("fontIndex") var fontIndex: String = ""
#AppStorage("showHVNumbers") var showHVNumbers: Bool = true
var body: some View {
ScrollViewReader { proxy in
List(lyrics, id: \.id) { lyric in
VStack(alignment: .center, spacing: 0) {
Text("\(lyric.number)")
.padding()
.multilineTextAlignment(/*#START_MENU_TOKEN#*/.leading/*#END_MENU_TOKEN#*/)
.id(lyric.number)
VStack {
VStack {
Text(lyric.zoTitle)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.primary)
.autocapitalization(.allCharacters)
Text(lyric.engTitle)
.font(.title3)
.fontWeight(.medium)
.foregroundColor(.secondary)
.italic()
}
// Don't use Padding here!
}
.multilineTextAlignment(.center)
HStack {
Text(lyric.key)
.italic()
Spacer()
Text(lyric.musicStyle)
}
.foregroundColor(.blue)
.padding(.vertical)
}
//.id(lyric.number)
}
.onChange(of: scrollToIndex, perform: { value in
proxy.scrollTo(value, anchor: .top)
})
}
}
}

How can I hide extra sized background View in a View?

I have a simple Rectangle which I put a VStack of Numbers in background of that, you can see it in code and pic! the problem is the VStack is taller than Rectangle there for it goes outside of Rectangle, I want the extra part be hidden, How could I solve it?
struct ContentView: View {
let arrayOfHours: [Int] = Array(0...23)
var body: some View {
Rectangle()
.fill(Color.black.opacity(0.25))
.frame(height: 200, alignment: .center)
.padding()
.background(
VStack(alignment: .center, spacing: 4) {
ForEach (arrayOfHours.indices, id: \.self) { index in
Text(arrayOfHours[index].description)
.font(Font.body.bold())
}
}
.background(Color.yellow)
)
}
}
If I understood your description correctly you need clipped, like
struct ContentView: View {
let arrayOfHours: [Int] = Array(0...23)
var body: some View {
Rectangle()
.fill(Color.black.opacity(0.25))
.frame(height: 200, alignment: .center)
.background(
VStack(alignment: .center, spacing: 4) {
ForEach (arrayOfHours.indices, id: \.self) { index in
Text(arrayOfHours[index].description)
.font(Font.body.bold())
}
}
.background(Color.yellow)
)
.clipped() // << here !!
.padding() // should be moved here !!
}
}

SwiftUI ActionSheet Picker

I'm trying to create in SwiftUI an action sheet that appears after pressing a button and allow the user to select and return an item throught a picker (like this https://imgur.com/a/IbS7swX).
Any hint on how to do it?
Thanks
struct ContentView: View {
init() {
UITableView.appearance().separatorColor = .clear
}
var inputArray = ["100","101","102"]
#State var slectedSegmant = "ActionSheet"
#State var slectedObj = "101"
#State var enableSheet = false
var test = false
var body: some View {
ZStack {
VStack(spacing: 10) {
Picker(selection: $slectedSegmant, label: Text("Segment")) {
Text("Alert").tag("Alert")
Text("ActionSheet").tag("ActionSheet")
}.pickerStyle(SegmentedPickerStyle())
.labelsHidden()
.padding(EdgeInsets.init(top: 50, leading: 10, bottom: 0, trailing: 10))
Text("Alert & Pickers")
.font(Font.system(size: 35, design: .rounded))
.fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
List((0...50),id: \.self) { input in
ZStack {
HStack(spacing: 10) {
Image(systemName: "book")
.font(.title)
.padding(.leading, 10)
VStack(alignment: .leading, spacing: 5, content: {
Text("Simple")
Text("3 different buttons")
})
Spacer()
}.padding(.vertical)
.frame(maxWidth:.infinity)
.background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.white).shadow(radius: 1.5)
)
Button(action: {
self.enableSheet = true
}) {
Text("")
}
}
}.padding()
}.blur(radius: $enableSheet.wrappedValue ? 1 : 0)
.overlay(
$enableSheet.wrappedValue ? Color.black.opacity(0.6) : nil
)
if $enableSheet.wrappedValue {
GeometryReader { gr in
VStack {
VStack {
Text("PickerView")
.font(.headline)
.foregroundColor(.gray)
.padding(.top, 10)
Text("Prefered ContentHeight")
.padding(.top, 5)
Picker("test", selection: self.$slectedObj) {
Text("100").id("100")
Text("101").id("101")
Text("101").id("102")
}.labelsHidden()
}.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color.white).shadow(radius: 1))
VStack {
Button(action: {
debugPrint("Done Selected")
self.enableSheet = false
}) {
Text("Done").fontWeight(Font.Weight.bold)
}.padding()
.frame(maxWidth: gr.size.width - 90)
.background(RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color.white).shadow(radius: 1))
}
}.position(x: gr.size.width / 2 ,y: gr.size.height - 200)
}
}
}.edgesIgnoringSafeArea(.all)
}
}
OUTPUT