I had some weird behavior in SwiftUI that I did not understand, and I simplified it to this:
struct ContentView: View {
#State var name: String = ""
var body: some View {
Form {
VStack {
Text("Line 1")
Text("Line 2")
Text("Line 3")
Text("Line 4")
Button(action: {
print("hello world")
}) { Text("Print hello world")}
}
}
}
}
This makes the "Line 1" ... "Line 4" texts be part of the "Print hello world" button.
If I remove the VStack in the Form they are not.
I am not sure if this is a bug in SwiftUI or if I am not understanding something that maybe I should try to understand, so: does anybody understand why Line 1 ... Line 4 would be part of the button here?
This is not a bug, Form is a List under the hood, so when you create a VStack in it you create a row in the list, but if you remove the VStack there are 5 rows (4 Texts and one Button)
Related
I have a simple layout, a VStack that has two children a Text and another HStack. and very weirdly SwiftUI applied a large padding beneath the text. and more weirdly the way to solve it is to add a padding but i have to set it to 0.1. this will solve the problem and it will be drawn correclty. I want to know is this just a mere bug or there is something i don't know about swiftUI and Stacks? here is the code and also the screenshots:
struct SettingScreen: View {
let navigationHeight : CGFloat
#Environment(\.locale) var locale : Locale
var isKurdish : Bool {
locale.identifier == "ku"
}
var body: some View {
VStack{
Text("Language")
.localeFont(font: .myTitle)
HStack{
RecheckSelectableButtonCard(
imageName: "flag_uk",
text: "English",
isActive: !isKurdish,
onCardClick: {}
)
.environment(\.locale, .init(identifier: "en-US"))
.padding(.trailing,16)
RecheckSelectableButtonCard(
imageName: "flag_kurdistan",
text: "کوردی",
isActive: isKurdish,
onCardClick: {}
)
.environment(\.locale, .init(identifier: "ku"))
}
.padding([.leading,.trailing],16)
Spacer()
}
.padding(.bottom,navigationHeight)
}
}
These cards are my custom cards and I'm 100% sure that the HStack and the cards are not the problem.
Here is the before adding padding to the text
And this is the after
it also had the same behavior when i run it on my phone
struct SettingScreen: View {
let navigationHeight : CGFloat
#Environment(\.locale) var locale : Locale
var isKurdish : Bool {
locale.identifier == "ku"
}
var body: some View {
VStack{
Text("Language")
.localeFont(font: .myTitle)
.padding(.bottom,0.1) // This solves the problem
HStack{
RecheckSelectableButtonCard(
imageName: "flag_uk",
text: "English",
isActive: !isKurdish,
onCardClick: {}
)
.environment(\.locale, .init(identifier: "en-US"))
.padding(.trailing,16)
RecheckSelectableButtonCard(
imageName: "flag_kurdistan",
text: "کوردی",
isActive: isKurdish,
onCardClick: {}
)
.environment(\.locale, .init(identifier: "ku"))
}
.padding([.leading,.trailing],16)
Spacer()
}
.padding(.bottom,navigationHeight)
}
}```
Can anyone explain to me is there another way to solve this issue ?
VStack applies its own spacing between items. There is no documentation on how it chooses its spacing, but it's trying to make it "good" on each platform. What it chooses can depend on the exact views, and likely adding your own .padding changes its mind about what it should do.
You can control the spacing when you create the VStack. I often find that I either want the default or zero:
VStack(spacing: 0) { ... }
With zero spacing from the VStack, you can manage it by hand with padding or fixed-sized Spacers if things get too close.
I was experimenting with layoutPriority() modifier. First I set its value as 1. Then I set like below:
layoutPriority(0)
I have not observed any changes. Can someone explain?
You use layoutPriority when there is a race where you need to prefer some ui over another like this one Hackingwithswift
struct ContentView: View {
var body: some View {
HStack {
Text("This line will take more space than the one below.")
Text("small short one.")
}
.font(.largeTitle)
}
}
But after adding
struct ContentView: View {
var body: some View {
HStack {
Text("This line will take more space than the one below.")
Text("small short one.").layoutPriority(1)
}
.font(.largeTitle)
}
}
I've created an card and I want an image to display on top of each card except for the first one. please help me to solve this.
This is the struct that holds the image.
struct XButton: View {
var body: some View {
Image(systemName: "xmark")
.font(.system(size: 17, weight: .bold))
.foregroundColor(.white)
.padding(.all, 10)
.background(Color.black.opacity(0.6))
.mask(Circle())
}
}
This the scrollview that contains the cards inside the foreach loop
ScrollView{
ForEach(CardsData) { item in
VStack {
CardsView(Cards: item)
}
}
}
You can use enumerated() to get the index and item during a ForEach. Then, XButton is only rendered conditionally if index == 0
Then, I use ZStack to put the XButton on top. I'm assuming "on top" in your original question means overlaid, but if you just meant higher on the y axis, you could change this back to a VStack
struct ContentView : View {
var body: some View {
ScrollView{
ForEach(Array(CardsData.enumerated()), id: \.1.id) { (index,item) in
ZStack {
CardsView(Cards: item)
if index == 0 {
XButton()
}
}
}
}
}
}
Note: this is assuming that CardsData items conform to Identifiable, which I assume they did if your original ForEach worked. Note that I'm getting the id from .1.id which is the id property of the item in the CardsData which is now a tuple where the first part (0) is the index and the second (1) is the item.
The Section transition in Form is always left-right, I would like to change it to right-left in some cases, I tried .transition with .move modifier, but it does not have effect.
struct ContentView: View {
#State var visible = true
var body: some View {
Form {
Button("visible") {
self.visible.toggle()
}
visible ? Section {
Text("Section 1")
Text("Section 1")
}.transition(.move(edge: .leading)) : nil
!visible ? Section {
Text("Section 2")
Text("Section 2")
}.transition(.move(edge: .trailing)) : nil
Section {
Text("Section 3")
Text("Section 3")
} // Section 3 should not be animated
}.animation(.linear(duration: 0.5))
}
}
I read through a significant amount of Apple's code documentation for SwiftUI forms and it appears that there are very few options for customization; specifically with the way you have the code currently set up. In that, I would recommend creating the animations and page from scratch. Doing it that way, you would have complete creative freedom of the directions for the transitions. This would take a lot of work to look like a Form, but it's possible. The following answer will give you a right-left transition: https://stackoverflow.com/a/62144939/13296047
For some reason, putting a GeometryReader as an intermediary, kills geometry of its nested views if it's a List "cell".
Example code:
struct SampleView: View {
var multilineText: some View {
Text(
"""
Some
Amazing
Multiline
Copy
"""
)
}
var body: some View {
List(1...5, id: \.self) { _ in
GeometryReader { _ in
self.multilineText
}
}
}
}
Without GeometryReader (Expected) / Actual with GeometryReader:
This example, obviously, is over-simplified, but there is a legitimate reason to measure geometry for a nested view I'm building.
This is on Xcode 11 beta 6. Should I go straight into reporting this as a bug, or it's something expected and workable around?
Add min row height for the list.
List(1...5, id: \.self) { _ in
GeometryReader { _ in
self.multilineText
}
}.environment(\.defaultMinListRowHeight, 100)