SwiftUI: Dismisses View when Binding of List state changes - swift

I have the following example in SwiftUI:
import SwiftUI
struct DetailView: View {
var element:Int
#Binding var favList:[Int]
var body: some View {
Button(action: {
if !favList.contains(element){
favList.append(element)
}
else{
favList.removeAll(where: {$0 == element})
}
}){
HStack {
Image(systemName: (favList.contains(element)) ? "star.slash" : "star")
Text((favList.contains(element)) ? "Remove from favorites" : "Add to favorites")
}
.frame(maxWidth: 300)
}
}
}
struct MainView: View {
let elements = [1,2,3,4]
#State var favList:[Int] = []
var body: some View {
NavigationView {
List{
if !favList.isEmpty{
Section("Favorits"){
ForEach(elements, id: \.self){element in
if favList.contains(element){
NavigationLink(destination: DetailView(element: element, favList: $favList)) {
Text("\(element)")
}
}
}
}
}
Section("All elements"){
ForEach(elements, id: \.self){element in
if !favList.contains(element){
NavigationLink(destination: DetailView(element: element, favList: $favList)) {
Text("\(element)")
}
}
}
}
}
}
}
}
If I change the favList in the DetailView the view gets automatically dismissed. I guess this because the List structure changes.
Am I doing something wrong? Is this the intended behavior? How can I avoid this?
Best regards

i tried with this, it's the same code just fixing the section header. using the fav button in the view doesn't dismiss the view
import SwiftUI
struct DetailView: View {
var element:Int
#Binding var favList:[Int]
var body: some View {
Button(action: {
if !favList.contains(element){
favList.append(element)
}
else{
favList.removeAll(where: {$0 == element})
}
}){
HStack {
Image(systemName: (favList.contains(element)) ? "star.slash" : "star")
Text((favList.contains(element)) ? "Remove from favorites" : "Add to favorites")
}
.frame(maxWidth: 300)
}
}
}
struct MainView: View {
let elements = [1,2,3,4]
#State var favList:[Int] = []
var body: some View {
NavigationView {
List{
if !favList.isEmpty{
Section(header:Text("Favorits")){
ForEach(elements, id: \.self){element in
if favList.contains(element){
NavigationLink(destination: DetailView(element: element, favList: $favList)) {
Text("\(element)")
}
}
}
}
}
Section(header:Text("Favorits")){
ForEach(elements, id: \.self){element in
if !favList.contains(element){
NavigationLink(destination: DetailView(element: element, favList: $favList)) {
Text("\(element)")
}
}
}
}
}
}
}
}
Tried it on xcode 12,5 with iOS 14 as target

Related

How to make Sidebar row remember its previous detail view progress for split view SwiftUI Mac catalyst?

I am creating Split View with NavigationView in SwiftUI MacCatalyst. In sidebar (master view), I have two rows: 'Add' and 'Profile'. Tapping on them changes the detail view.
Suppose I click on 'Add' row, I see AddView() in detail view.
Then I tap 'Next 1' button to show 'TempView 1' on navigation stack.
Now, I tap on 'Profile' row in sidebar.
And when I tap one 'Add' row in sidebar again, I see this:
instead of this:
Does anyone know how to preserve row stages for SplitView in SwiftUI? My sample code below:
import SwiftUI
struct AddView: View {
#State var showNext = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: TempView1(), isActive: $showNext) { EmptyView() }
Button("Next 1") {
showNext = true
}
Text("Add View")
.padding()
}
}
.navigationViewStyle(.stack)
}
}
struct ProfileView: View {
#State var showNext = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: TempView2(), isActive: $showNext) { EmptyView() }
Button("Next 2") {
showNext = true
}
Text("Profile View")
}
}
.navigationViewStyle(.stack)
}
}
struct TempView1 : View {
var body: some View {
Text("Temp View 1")
}
}
struct TempView2 : View {
var body: some View {
Text("Temp View 2")
}
}
struct ContentView: View {
#State var showAddView = false
#State var showProfileView = false
#State var selectedRow = -1
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: AddView(), isActive: $showAddView) { EmptyView() }
NavigationLink(destination: ProfileView(), isActive: $showProfileView) { EmptyView() }
List {
Text("Add")
.padding()
.background(selectedRow == 0 ? Color.yellow : Color.clear)
.onTapGesture {
selectedRow = 0
showAddView = true
}
Text("Profile")
.padding()
.background(selectedRow == 1 ? Color.yellow : Color.clear)
.onTapGesture {
selectedRow = 1
showProfileView = true
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

SwiftUI - Form Picker - How to prevent navigating back on selected?

I'm implementing Form and Picker with SwiftUI. There is a problem that it automatically navigates back to Form screen when I select a Picker option, how to keep it stay in selection screen?
Code:
struct ContentView: View {
#State private var selectedStrength = "Mild"
let strengths = ["Mild", "Medium", "Mature"]
var body: some View {
NavigationView {
Form {
Section {
Picker("Strength", selection: $selectedStrength) {
ForEach(strengths, id: \.self) {
Text($0)
}
}
}
}
.navigationTitle("Select your cheese")
}
}
}
Actual:
Expect: (sample from Iphone Settings)
You may have to make a custom view that mimics what the picker looks like:
struct ContentView: View {
let strengths = ["Mild", "Medium", "Mature"]
#State private var selectedStrength = "Mild"
var body: some View {
NavigationView {
Form {
Section {
NavigationLink(destination: CheesePickerView(strengths: strengths, selectedStrength: $selectedStrength)) {
HStack {
Text("Strength")
Spacer()
Text(selectedStrength)
.foregroundColor(.gray)
}
}
}
}
.navigationTitle("Select your cheese")
}
}
}
struct CheesePickerView: View {
let strengths: [String]
#Binding var selectedStrength: String
var body: some View {
Form {
Section {
ForEach(0..<strengths.count){ index in
HStack {
Button(action: {
selectedStrength = strengths[index]
}) {
HStack{
Text(strengths[index])
.foregroundColor(.black)
Spacer()
if selectedStrength == strengths[index] {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
}.buttonStyle(BorderlessButtonStyle())
}
}
}
}
}
}

Button with NavigationLink and function call SwiftUI

I have an issue with NavigationLinks with conditions. It doesn't react what I'm expected. When a user click on the button the function "test" must be called and give a return value. If the return value is true the "SheetView" must be openend directly without clicking on the NavigationLink text. Please could someone give me a help on this one. Thanks in advance
I made a small (sample) program for showing the issue.
import SwiftUI
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
Button(action: {
answer = test(value: 2)
if answer {
//if the return value is true then directly navigate to the sheetview
NavigationLink(
destination: SheetView(),
label: {
Text("Navigate")
})
}
}, label: {
Text("Calculate")
})
}
}
}
func test(value: Int) -> Bool {
if value == 1 {
check = false
} else {
print("Succes")
check = true
}
return check
}
}
struct SheetView: View {
var body: some View {
NavigationView {
VStack{
Text("Test")
.font(.title)
}
}
}
}
The answer from Yodagama works if you were trying to present a sheet (because you called your navigation destination SheetView), but if you were trying to navigate to SheetView instead of present a sheet, the following code would do that.
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
NavigationLink(destination: SheetView(), isActive: $answer, label: {
Button(action: {
answer = test(value: 2)
}, label: {
Text("Calculate")
})
})
}
}
}
func test(value: Int) -> Bool {
if value == 1 {
check = false
} else {
print("Succes")
check = true
}
return check
}
}
struct SheetView: View {
var body: some View {
NavigationView {
VStack{
Text("Test")
.font(.title)
}
}
}
}
struct LoginView: View {
#State var check = false
#State var answer = false
var body: some View {
NavigationView {
VStack{
Text("it doesn't work")
Button(action: {
answer = test(value: 2)
//<= here
}, label: {
Text("Calculate")
})
}
.sheet(isPresented: $answer, content: { //<= here
SheetView()
})
}
}
...

SwiftUI: Unable to access Edit Mode within TabView

I currently have a view HostView that provides HostSummaryView and EditHostSummaryView, where the former responds to editMode.wrappedValue? == .inactive. HostView looks like this:
struct HostView: View {
#Environment(\.editMode) var editMode
var body: some View {
HStack {
EditButton()
}
if editMode?.wrappedValue == .inactive {
HostSummary()
} else {
EditHostSummary()
}
}
}
I have a RootView that contains a TabView, which looks like this:
struct RootView: View {
#State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
View1()
.onTapGesture { self.selectedTab = 0 }
.tag(0)
View2()
.tag(1)
HostView()
.tag(2)
}
}
}
I tried passing the #Environment(\.editMode) var editMode to HostView, but that did not fix the problem. The EditButton does not toggle editMode in the HostView. However, HostView works when I access it through a non-TabView view.
How can I get this to work?
I couldn't find a previous question about this but it is known that some things don't work from within a TabView you have to push it down a View.
I think it is considered a bug.
struct EditableHost: View {
#State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
Text("View 1")
.onTapGesture { self.selectedTab = 0 }
.tag(0)
Text("View 2")
.tag(1)
ParentHostView()
.tabItem { Text("host") }
.tag(2)
}
}
}
struct ParentHostView: View {
#State var active: Bool = true
var body: some View {
NavigationView{
NavigationLink(
destination: HostView(),
isActive: $active,
label: {
Text("HOST")
})
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct HostView: View {
#Environment(\.editMode) var editMode
var body: some View {
VStack{
HStack {
EditButton()
}
if editMode?.wrappedValue == .inactive {
Text("HostSummary")
} else {
Text("EditHostSummary")
}
}.navigationBarBackButtonHidden(true)
}
}
I encounter the same case(Xcode 13.3, iOS 15.4). You can customize an edit button and everything should be ok.
struct HostView: View {
#Environment(\.editMode) var editMode
var body: some View {
HStack {
Button {
if editMode != nil{
if editMode!.wrappedValue == .inactive{
editMode!.wrappedValue = .active
}else{
if editMode!.wrappedValue == .active{
editMode!.wrappedValue = .inactive
}
}
}
} label: {
Text(editMode?.wrappedValue.isEditing == true ? "Done" : "Edit")
}
}
if editMode?.wrappedValue == .inactive {
HostSummary()
} else {
EditHostSummary()
}
}
}

SwiftUI: How can I move texts or something else with VStack, HStack?

I'm new in SwiftUI. I would like on the top left in the corner a Button next to the Picker. But when I place the Button, the Picker moves to the right and the Button is to nearly on the edge. How can I place the Button flush over the Headline and the Picker perfectly in the middle from iPhone Nodge?
Before:
After:
import SwiftUI
import Combine
struct ContentView: View {
#State var Selection = UserDefaults.standard.integer(forKey: "Picker")
#State var Detail = false
var body: some View {
VStack {
HStack {
Button(action: {
self.Detail.toggle()
}) {
Text("click")
}.sheet(isPresented: $Detail) {
SettingView(showSheetView: self.$Detail, selection: $Selection)
}
Picker("", selection: $Selection) {
Text("Selection1").tag(0)
Text("Selection2").tag(1)
}
.pickerStyle(SegmentedPickerStyle()).padding(.horizontal, 89)
.onReceive(Just(Selection)) {
UserDefaults.standard.set($0, forKey: "Picker")
}
}
PageOne()
}
}
}
struct PageOne: View {
var body: some View {
NavigationView {
VStack {
Text("some Text")
}.navigationTitle("Headline")
}
}
}
struct SettingView: View {
#Binding var showSheetView: Bool
#Binding var selection: Int
var body: some View {
NavigationView {
Text("Test")
.navigationBarTitle(Text("Select something"))
.navigationBarItems(trailing: Button(action: {
self.showSheetView = false
}) {
Text("Ok")
.bold()
})
}
}
}
you can use a ZStack to show your Button and Picker without pushing them.
something like this:
var body: some View {
VStack {
ZStack {
HStack {
Button(action: {
self.Detail.toggle()
}) {
Text("click")
}.sheet(isPresented: $Detail) {
SettingView(showSheetView: self.$Detail, selection: $Selection)
}
Spacer()
}
HStack {
Spacer()
Picker("", selection: $Selection) {
Text("Selection1").tag(0)
Text("Selection2").tag(1)
}
.pickerStyle(SegmentedPickerStyle()).padding(.horizontal, 89)
.onReceive(Just(Selection)) {
UserDefaults.standard.set($0, forKey: "Picker")
}
Spacer()
}
}
PageOne()
}
}