How the layoutPriority values work in SwiftUI? - ios

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)
}
}

Related

ContentView not expanding outside of overlay - SwiftUI

I want to display a view as a popup/tooltip from a view. I beleive the best way to acheive this is by presenting it as an overlay. But, the view is not expanding outside of bounds of where its being presented.
import SwiftUI
struct ContentView: View {
let message = "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)"
var body: some View {
VStack {
Button {
} label: {
Text("Tap Me")
.background(
Rectangle()
.fill(.red)
)
}
.overlay {
contentView
}
}
.padding()
}
var contentView: some View {
Text(message)
.foregroundColor(Color.white)
.padding()
.background(Color.black)
.foregroundColor(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 5))
.offset(y: 60)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
return ContentView()
}
}
Why is the contentView not expanding outside of the view from where it is being overlayed in my SwiftUI code?
I tried setting fixedSize, frame(maxWidth, but none of them have correct behaviour.
The .overlay modifier always takes the size of its parent view as its maximum size. To put any size view in front of another, you should use a ZStack, e.g.
ZStack {
Button {
} label: {
Text("Tap Me")
.background(
Rectangle()
.fill(.red)
)
}
contentView
}
This is what it looks like (with .opacity applied)

Why there is a padding beneath a text in swiftUI when i have applied none? is this a bug or a feature?

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.

What is the correct way to pass touches through a view to those below it?

I have a SwiftUI view that is displayed over other views, and have found that using Color.clear like this below seems to allow touch interactions to pass through to anything under it:
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
SomeCustomContent()
Spacer()
}
.overlay(GeometryReader { proxy in
Color.clear.preference(key: MyCustomHeightPreferenceKey.self, value: proxy.size.height)
})
}
}
Is this the correct way to make touches pass through to the views below, or it this just a coincidental quirk/bug in SwiftUI behaviour that Apple might fix/change as swiftui matures?
If not, what is the correct way to pass the touches through?
You can pass through touch events without use a clear color like this:
var body: some View {
Rectangle()
.overlay(
Circle()
.fill(.blue)
.allowsHitTesting(false) // <--- Passes through gestures
)
}
Asperi mentioned this solution in a comment above, and you can also find a good blog about this topic here: https://www.hackingwithswift.com/books/ios-swiftui/disabling-user-interactivity-with-allowshittesting

Section transition in SwiftUI Form

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

Not possible to control animations in a Form?

Is it not meant to be possible to control the animations that take place inside of a Form view? I have here a playground that demonstrates the issue, along with a gif of what happens. As you can see, my transition on the 2nd animated view is completely ignored, and I had to manually slow down the video because the durations are ignored, too.
I don't really want a scaling transition, this was just to demonstrate that no matter what I put in there the animation is the same. Is that expected, or is it a bug? Or am I just doing something totally wrong?
It's also not clear to me why the animation of the VStack is handled so differently than the simple Text field, which slides down nicely while the VStack seems to be getting some combination of .move and .opacity.
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
#State var showGoodAnimation = false
#State var showBadAnimation = false
var body: some View {
Form {
Toggle(isOn: self.$showGoodAnimation.animation(.easeInOut(duration: 1))) {Text("Yay!")}
if self.showGoodAnimation {
Text("I animate beautifully.")
}
Toggle(isOn: self.$showBadAnimation.animation(.easeInOut(duration: 1))) {Text("Boo!")}
if self.showBadAnimation {
VStack {
Text("Hi.").padding()
Text("I'm a hot mess.").padding()
}
.frame(height: 250)
.transition(.scale)
}
Text("I'm just always here.")
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
At a guess, probably worked around this question some time ago, but for the benefit of those beating their head's against SwiftUI Form and the like now (as I was :-) )
It turns out that Forms, Lists and (no doubt) other components purposely ignore animation customisation because they are "higher-level" SwiftUI View components (unlike V and HStack's).
They do this because SwiftUI's higher-level components are intended to convey semantic information and (more practically) to work well across all platforms. To achieve this, Apple's engineering decision has been to make animation "easy", but (as observed) only to the extent of essentially turning it "on" or "off".
It's likely engineered like this because if a developer wants more control. Then Apple believes encouraging them to use the lower-level components will be less painful than trying to work around the optimisations they have applied to their higher-level view components.
Anyway, for the determined, there is at least one escape hatch via wrapping the View in a container and specifying the .animation(nil) modifier (as mentioned in Asperi's SO answer here.
An example of this is shown below for completeness; personally, I'm avoiding this pattern as I suspect it's a bit of a footgun.
import PlaygroundSupport
import SwiftUI
struct ContentView: View {
#State var showGoodAnimation = false
#State var showBadAnimation = false
var body: some View {
Form {
Toggle(isOn: self.$showGoodAnimation.animation(.easeInOut(duration: 1))) { Text("Yay!") }
if self.showGoodAnimation {
Text("I animate beautifully.")
}
Toggle(isOn: self.$showBadAnimation.animation()) { Text("Boo!") }
VStack {
if self.showBadAnimation {
List {
Text("I animated differently").padding()
Text("But am I a footgun?").padding()
}
.transition(.asymmetric(insertion: .slide, removal: .opacity))
.animation(.easeOut(duration: 5))
}
}
.animation(nil)
.transition(.slide)
Text("I'm just always here.")
}
}
}
PlaygroundPage.current.setLiveView(ContentView())

Resources