ZStack is not changing color - ios

I've added Color.orange to my ZStack - but my view still has the default white/greyish background:
struct Settings: View {
#State var minAge = UserSettings().minAge
#State var maxAge = UserSettings().maxAge
#State var chosenSeeking = UserSettings.Seeking.both
var body: some View {
ZStack {
Color.orange
VStack {
NavigationView {
Form {
Section {
Picker("Look for", selection: $chosenSeeking) {
ForEach(UserSettings.Seeking.allCases) { i in
Text(String(i.rawValue))
}
}
}
Section {
Text("Min age")
Slider(value: $minAge, in: 18...99, step: 1, label: {Text("Label")})
Text(String(Int(minAge)))
}
Section {
Text("Max age")
Slider(value: $maxAge, in: 18...99, step: 1)
Text(String(Int(maxAge)))
}
}.navigationBarTitle(Text("Settings"))
}
}
}
}
}
Any idea what the problem is?

Best I could find was the colorMultiply:
NavigationView {
...
}.colorMultiply(.orange)

Could you try editing your code below format?
I put ZStack under NavigationView, and in this case, the background color changes to orange.
NavigationView{
ZStack{
Color.orange.edgesIgnoringSafeArea(.all)
VStack{
//some code
}
}
}

Your problem is that your NavigationView blocks the orange color. You would have to change the background of the NavigationView itself. With default views such as NavigationView, this is typically done by implementing a custom style of that view. In the case of Button that would be ButtonStyle. NavigationView does have NavigationViewStyle, however this is not yet publicly available. Our best hope might be the next major SwiftUI iteration, which will most likely be announced at WWDC this month.

Related

SwiftUI: fade out view

I have the following code:
struct ContentView: View {
#State var show = false
var body: some View {
VStack {
ZStack {
Color.black
if show {
RoundedRectangle(cornerRadius: 20)
.fill(.brown)
.transition(.opacity)
}
}
Button {
withAnimation(.easeInOut(duration: 1)) {
show.toggle()
}
} label: {
Text("TRIGGER")
}
}
}
}
I want the RoundedRectangle to fade in and out. Right now it only fades in. This is a simplified version of a more complex view setup I have. Depending on the state I may have the view I want to fade in or not. So, I am looking for a way to fade in (like it works now) but then also fade out so that the view is totally removed from the hierarchy and not just hidden or something.
How can I have this code also fade OUT the view and not only fade in?
As a reference I followed this approach:
https://swiftui-lab.com/advanced-transitions/
....
if show {
LabelView()
.animation(.easeInOut(duration: 1.0))
.transition(.opacity)
}
Spacer()
Button("Animate") {
self.show.toggle()
}.padding(20)
....
But, in my case it is NOT fading out.
SwiftUI ZStack transitions are finicky. You need to add a zIndex to make sure the hierarchy is preserved, enabling the animation.
RoundedRectangle(cornerRadius: 20)
.fill(.brown)
.transition(.opacity)
.zIndex(1) /// here!
You need to link the opacity directly to the state, so that it is directly animating any changes.
struct ContentView: View {
#State var show = false
var body: some View {
VStack {
ZStack {
Color.black
(RoundedRectangle(cornerRadius: 20)
.fill(.brown)
.opacity(show ? 1 : 0)
)
}
Button {
withAnimation(.easeInOut(duration: 1)) {
show.toggle()
}
} label: {
Text("TRIGGER")
}
}
}
}
EDIT: to reflect the comment requiring the view to be removed, not just faded out...
To remove the view (and trigger .onDisappear) you could modify as below:
ZStack {
Color.black
show ? (RoundedRectangle(cornerRadius: 20)
.fill(.brown)
.zIndex(1). //kudos to #aheze for this!
).onDisappear{print("gone")}
: nil
}
This will fade in/out as above, but will actually remove the view & print "gone"

Why background color of List is different while presenting view in SwiftUI?

I am implementing List in Presented view (AddItemView). I want background color same as List in any view.
struct HomeView: View {
#State private var showAddItemView: Bool = false
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}
.navigationTitle("Home")
.navigationBarItems(trailing:
Button("Add") {
showAddItemView.toggle()
})
.sheet(isPresented: $showAddItemView) {
AddItemView()
}
}
}
}
struct AddItemView: View {
init(){
UITableView.appearance().backgroundColor = .clear
}
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}.background(Color(UIColor.systemGroupedBackground))
.listStyle(InsetGroupedListStyle())
.navigationBarTitle("Add Item View", displayMode: .inline)
}
}
}
Above code is creating simple List with InsetGroupedListStyle. But background colour is different while Presenting view (AddItemView in my case).
I have already tried https://stackoverflow.com/a/58427518/7084910
How to set background color of List in presented view as in any normal list. Red/Yellow/Green can set to List, "BUT" I want same as normal list in HomeView that will work in light & dark mode.
Use this:
var body: some View {
NavigationView {
List(0..<9, id: \.self) { i in
Text("Row \(i)")
}
.colorMultiply(Color.red)
}
}
They think it is better visual representation for .sheet (probably to make it more determinable)...
SwiftUI 2.0
The .fullScreenCover gives what you want. Alternate is to present AddItemView manually using some transition.
.navigationBarItems(trailing:
Button("Add") {
showAddItemView.toggle()
})
.fullScreenCover(isPresented: $showAddItemView) {
AddItemView()
}

Fix odd DatePicker animation behaviour in SwiftUI form

I'm getting some odd animation behaviour with DatePickers in a SwiftUI form. A picture is worth a thousand words, so I'm sure a video is worth a million words: https://imgur.com/a/UHXqXOh
I'm trying to get the date picker to expand and then collapse within the form, exactly like the behaviour when creating a new event in Calendar.app
What is happening for me is:
Any expanding item in a Section (other than the last one) will open normally, but when it closes the expanded part slides down and fades, instead of sliding up and fading.
The last item in the section slides correctly but doesn't fade at all. It simply appears and then disappears at the start/end of the transition
These behaviours only happen if there is a non-DatePicker element (e.g. Text, Slider) somewhere in the form (doesn't have to be in that particular section)
Here's my ContentView:
struct ContentView: View {
#State var date = Date()
#State var isDateShown = false
var body: some View {
Form {
Section(header: Text("Title")) {
DatePicker("Test", selection:$date)
DatePicker("Test", selection:$date)
Text("Pick a date").onTapGesture {
withAnimation {
self.isDateShown.toggle()
}
}
if(isDateShown) {
DatePicker("", selection: $date).datePickerStyle(WheelDatePickerStyle()).labelsHidden()
}
}
Section(header: Text("hello")) {
Text("test")
}
}
}
}
Happy to provide anything else required
Here are two possible workarounds for iOS <14: 1) simple one is to disable animation at all, and 2) complex one is to mitigate incorrect animation by injecting custom animatable modifier
Tested both with Xcode 11.4 / iOS 13.4
1) simple solution - wrap DatePicker into container and set animation to nil
VStack {
DatePicker("Test", selection:$date).id(2)
}.animation(nil)
2) complex solution - grab DatePicker changing frame using a) view preference reader ViewHeightKey and b) animate this frame explicitly using AnimatingCellHeight from my other solutions.
struct TestDatePickersInForm: View {
#State var date = Date()
#State var isDateShown = false
#State private var height = CGFloat.zero
var body: some View {
Form {
Section(header: Text("Title")) {
// demo of complex solution
VStack {
DatePicker("Test", selection:$date).id(1)
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height) })
}
.onPreferenceChange(ViewHeightKey.self) { self.height = $0 }
.modifier(AnimatingCellHeight(height: height))
.animation(.default)
// demo of simple solution
VStack {
DatePicker("Test", selection:$date).id(2)
}.animation(nil)
Text("Pick a date").onTapGesture {
withAnimation {
self.isDateShown.toggle()
}
}
if(isDateShown) {
DatePicker("", selection: $date).datePickerStyle(WheelDatePickerStyle()).labelsHidden().id(3)
}
}
Section(header: Text("hello")) {
Text("test")
}
}
}
}
Funny enough.. with the new beta, they apparently changed the DatePicker.
So if you have no problem with iOS 14+ only...
closest solution is to move the datepickers to its own sections
Form {
Section(header: Text("Title")) {
DatePicker(selection:$date1, label: {Text("Test")} )
}
DatePicker("Test", selection:$date2)
Section{
Text("Pick a date").onTapGesture {
withAnimation {
self.isDateShown.toggle()
}
}
if(isDateShown) {
DatePicker("", selection: $date3).datePickerStyle(WheelDatePickerStyle()).labelsHidden()
}
}
Section(header: Text("Hello")){
Text("Hello")
}
}

Transition animation not working in SwiftUI

I'm trying to create a really simple transition animation that shows/hides a message in the center of the screen by tapping on a button:
struct ContentView: View {
#State private var showMessage = false
var body: some View {
ZStack {
Color.yellow
VStack {
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 3)) {
self.showMessage.toggle()
}
}) {
Text("SHOW MESSAGE")
}
}
if showMessage {
Text("HELLO WORLD!")
.transition(.opacity)
}
}
}
}
According to the documentation of the .transition(.opacity) animation
A transition from transparent to opaque on insertion, and from opaque
to transparent on removal.
the message should fade in when the showMessage state property becomes true and fade out when it becomes false. This is not true in my case. The message shows up with a fade animation, but it hides with no animation at all. Any ideas?
EDIT: See the result in the gif below taken from the simulator.
The problem is that when views come and go in a ZStack, their "zIndex" doesn't stay the same. What is happening is that the when "showMessage" goes from true to false, the VStack with the "Hello World" text is put at the bottom of the stack and the yellow color is immediately drawn over top of it. It is actually fading out but it's doing so behind the yellow color so you can't see it.
To fix it you need to explicitly specify the "zIndex" for each view in the stack so they always stay the same - like so:
struct ContentView: View {
#State private var showMessage = false
var body: some View {
ZStack {
Color.yellow.zIndex(0)
VStack {
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 3)) {
self.showMessage.toggle()
}
}) {
Text("SHOW MESSAGE")
}
}.zIndex(1)
if showMessage {
Text("HELLO WORLD!")
.transition(.opacity)
.zIndex(2)
}
}
}
}
My findings are that opacity transitions don't always work. (yet a slide in combination with an .animation will work..)
.transition(.opacity) //does not always work
If I write it as a custom animation it does work:
.transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.2)))
.zIndex(1)
I found a bug in swiftUI_preview for animations. when you use a transition animation in code and want to see that in SwiftUI_preview it will not show animations or just show when some views disappear with animation. for solving this problem you just need to add your view in preview in a VStack. like this :
struct test_UI: View {
#State var isShowSideBar = false
var body: some View {
ZStack {
Button("ShowMenu") {
withAnimation {
isShowSideBar.toggle()
}
}
if isShowSideBar {
SideBarView()
.transition(.slide)
}
}
}
}
struct SomeView_Previews: PreviewProvider {
static var previews: some View {
VStack {
SomeView()
}
}
}
after this, all animations will happen.
I believe this is a problem with the canvas. I was playing around with transitions this morning and while the don't work on the canvas, they DO seem to work in the simulator. Give that a try. I've reported the bug to Apple.
I like Scott Gribben's answer better (see below), but since I cannot delete this one (due to the green check), I'll just leave the original answer untouched. I would argue though, that I do consider it a bug. One would expect the zIndex to be implicitly assigned by the order views appear in code.
To work around it, you may embed the if statement inside a VStack.
struct ContentView: View {
#State private var showMessage = false
var body: some View {
ZStack {
Color.yellow
VStack {
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 3)) {
self.showMessage.toggle()
}
}) {
Text("SHOW MESSAGE")
}
}
VStack {
if showMessage {
Text("HELLO WORLD!")
.transition(.opacity)
}
}
}
}
}
zIndex may cause the animation to be broken when interrupted. Wrap the view you wanna apply transition to in a VStack, HStack or any other container will make sense.
I just gave up on .transition. It's just not working. I instead animated the view's offset, much more reliable:
First I create a state variable for offset:
#State private var offset: CGFloat = 200
Second, I set the VStack's offset to it. Then, in its .onAppear(), I change the offset back to 0 with animation:
VStack{
Spacer()
HStack{
Spacer()
Image("MyImage")
}
}
.offset(x: offset)
.onAppear {
withAnimation(.easeOut(duration: 2.5)) {
offset = 0
}
}
Below code should work.
import SwiftUI
struct SwiftUITest: View {
#State private var isAnimated:Bool = false
var body: some View {
ZStack(alignment:.bottom) {
VStack{
Spacer()
Button("Slide View"){
withAnimation(.easeInOut) {
isAnimated.toggle()
}
}
Spacer()
Spacer()
}
if isAnimated {
RoundedRectangle(cornerRadius: 16).frame(height: UIScreen.main.bounds.height/2)
.transition(.slide)
}
}.ignoresSafeArea()
}
}
struct SwiftUITest_Previews: PreviewProvider {
static var previews: some View {
VStack {
SwiftUITest()
}
}
}

How to change NavigationViewStyle from a descendent view in SwiftUI?

I have a NavigationView at the root of my app that has the initial state of .navigationViewStyle(.stack), but as I navigate, I want to change that style to .doubleColumn.
From my descendent view I have tried calling navigationViewStyle(.doubleColumn) much like how you change the navigation bar title, but no luck.
Trying a ternary operator in the root view also doesn't work navigationViewStyle(isDoubleColumn ? .doubleColumn : .stack)
ROOT VIEW:
var body: some View {
NavigationView {
VStack {
//stuff
}
}
.navigationViewStyle(.stack)
}
Descendent View
var body: some View {
ScrollView{
//stuff
}
.navigationViewStyle(.doubleColumn) //doesn't work
}
DoubleColumnNavigationViewStyle is a struct that needs to be initialized. Try it this way:
.navigationViewStyle(DoubleColumnNavigationViewStyle())
The code below does the job but with the price of losing of some state data so it may not really be the solution.
struct SwiftUIView: View {
#State var isDoubleColumn = false
var body: some View {
Group {
if isDoubleColumn{
NavigationView {
Text("Hello, World!")
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())
} else {
NavigationView {
Text("Hello, World!")
}
}
}
}
}
I'm not an expert but something tells me that the style can't be changed during NavigationView lifetime.

Resources