I have a problem with my code.
#EnvironmentObject var modelData: FirebaseModelData
#State var selectionString = ""
#State var isPresented = false
#State var selectedMaster: Master?
var body: some View {
ScrollView {
VStack(spacing: 20.0) {
LocationSelector()
Searchbar()
Text("Фильтр по услугам")
.font(.title3)
ServiceTypeSelection(selectionString: $selectionString)
VStack {
if selectionString != "" {
ForEach(0 ..< modelData.masters.count, id: \.self) { master in
if modelData.masters[master].serviceTypes.contains(selectionString) {
Button {
self.selectedMaster = modelData.masters[master]
isPresented.toggle()
} label: {
MasterRow(master: modelData.masters[master])
}
}
}
.buttonStyle(PlainButtonStyle())
} else {
ForEach(0 ..< modelData.masters.count, id: \.self) { master in
Button {
self.selectedMaster = modelData.masters[master]
print(self.selectedMaster)
isPresented.toggle()
} label: {
MasterRow(master: modelData.masters[master])
}
}
.buttonStyle(PlainButtonStyle())
}
}
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
.sheet(isPresented: $isPresented) {
MasterView(master: self.selectedMaster!)
.environment(\.modalMode, self.$isPresented)
}
}
}
State value selectedMaster is supposed to be changed on button click, but it doesn't and as a result my program crashes. Please help me, I have no idea why this keeps happening.
Additionally, I tried changing the type of selectedMaster to Int and pass the required data object as self.selectedMaster = modelData.masters[master], but that didn't work either.
If you have any additional questions, please feel free to ask!
Thanks in advance.
as #aheze suggested, you could try the .sheet(item...), like this:
.sheet(item: $selectedMaster, onDismiss: {isPresented = false}) { theMaster in
MasterView(master: theMaster)
.environment(\.modalMode, self.$isPresented)
}
Related
I have my variables working and I have an array I want to cycle through to add text to a list of each of the variables in my array. Any ideas on what im doing wrong and how I can fix it? Thanks.
Here is my code:
struct ContentView: View {
#State var enteredText = ""
#State var textItems: [String] = []
private func updateTextItems(){
textItems.append(enteredText)
print("---------------")
for i in textItems {
print(i)
}
print("---------------")
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Add Item to Array")) {
VStack {
TextField("Enter Text", text: $enteredText)
Button("Save", action: updateTextItems)
.buttonStyle(.bordered)
}
Section(header: Text("Items")){
ForEach(textItems) {textItems in
HStack{
Text(textItems)
}
}
}
}
}
.navigationTitle("Array Test")
}
}
}
first of all that foreach statement needs to fire compile error :). I think this one is working :)
struct TestView: View {
#State var enteredText = ""
#State var textItems: [String] = []
private func updateTextItems(){
textItems.append(enteredText)
print("---------------")
for i in textItems {
print(i)
}
print("---------------")
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Add Item to Array")) {
VStack {
TextField("Enter Text", text: $enteredText)
Button("Save", action: updateTextItems)
.buttonStyle(.bordered)
}
Section(header: Text("Items")){
ForEach(textItems, id: \.self) {textItems in
HStack{
Text(textItems)
}
}
}
}
}
.navigationTitle("Array Test")
}
}
}
I am trying to make a SwiftUI ScrollView scroll to a certain point in an abstracted view when a button is pressed in a view which is calling the abstracted view programmatically. Here is my code:
struct AbstractedView: View {
#Namespace var view2ID
var body: some View {
ScrollView {
VStack {
View1()
View2()
.id(view2ID)
View3()
}
}
}
func scrollToView2(_ proxy: ScrollViewProxy) {
proxy.scrollTo(view2ID, anchor: .topTrailing)
}
}
As you can see, when scrollToView2() is called (in a ScrollViewReader), the AbstractedView scrolls to view2ID. I am creating a number of AbstractedView's programmatically in a different View:
struct HigherView: View {
var numAbstractedViewsToMake: Int
var body: some View {
VStack {
HStack {
ForEach (0..<numAbstractedViewsToMake, id: \.self) { _ in
AbstractedView()
}
}
Text("button")
.onTapGesture {
/* call each AbstractedView.scrollToView2()
}
}
}
}
If I stored these views in an array in a struct inside my HigherView with a ScrollViewReader for each AbstractedView would that work? I feel as though there has to be a nicer way to achieve this, I just have no clue how to do it. I am new to Swift so thank you for any help.
P.S. I have heard about UIKit but I don't know anything about it, is this the right time to be using that?
Using the comments from #Asperi and #jnpdx, I was able to come up with a more powerful solution than I needed:
class ScrollToModel: ObservableObject {
enum Action {
case end
case top
}
#Published var direction: Action? = nil
}
struct HigherView: View {
#StateObject var vm = ScrollToModel()
var numAbstractedViewsToMake: Int
var body: some View {
VStack {
HStack {
Button(action: { vm.direction = .top }) { // < here
Image(systemName: "arrow.up.to.line")
.padding(.horizontal)
}
Button(action: { vm.direction = .end }) { // << here
Image(systemName: "arrow.down.to.line")
.padding(.horizontal)
}
}
Divider()
HStack {
ForEach(0..<numAbstractedViewsToMake, id: \.self) { _ in
ScrollToModelView(vm: vm)
}
}
}
}
}
struct AbstractedView: View {
#ObservedObject var vm: ScrollToModel
let items = (0..<200).map { $0 } // this is his demo
var body: some View {
VStack {
ScrollViewReader { sp in
ScrollView {
LazyVStack { // this bit can be changed accordingly
ForEach(items, id: \.self) { item in
VStack(alignment: .leading) {
Text("Item \(item)").id(item)
Divider()
}.frame(maxWidth: .infinity).padding(.horizontal)
}
}.onReceive(vm.$direction) { action in
guard !items.isEmpty else { return }
withAnimation {
switch action {
case .top:
sp.scrollTo(items.first!, anchor: .top)
case .end:
sp.scrollTo(items.last!, anchor: .bottom)
default:
return
}
}
}
}
}
}
}
}
Thank you both!
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
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()
})
}
}
...
I don't understand how implementing in SwiftUI a simple picker showing a list of values that retain the selected value switching between different views. I'm able to use the selected value to update the Model via Combine framework by the way.
here's the code, but the onAppear{}/onDisappear{} doesn't work as expected:
struct CompanyView: View {
#ObservedObject var dataManager: DataManager = DataManager.shared
#State var selTipoAzienda = 0
var body: some View {
VStack {
companyPhoto
Text("Company view")
Form {
Picker(selection: $selTipoAzienda, label: Text("Tipo Azienda")) {
ForEach(0 ..< self.dataManager.company.tipoAziendaList.count) {
Text(self.dataManager.company.tipoAziendaList[$0])
}
}
}
Button(action: {self.dataManager.cambiaTipoAzienda(tipoAzienda: self.dataManager.company.tipoAziendaList[self.selTipoAzienda]) }) {
Image(systemName: "info.circle.fill")
.font(Font.system(size: 28))
.padding(.horizontal, 16)
}
}
// .onAppear{
// self.selTipoAzienda = self.dataManager.company.tipoAziendaList.firstIndex(of: self.dataManager.company.tipoAzienda) ?? 0
// }
// .onDisappear{
// self.dataManager.cambiaTipoAzienda(tipoAzienda: self.dataManager.company.tipoAziendaList[self.selTipoAzienda])
// }
}
I think binding and didSet would be the answer but I don't know how they have to be implemented
The provided code is not compilable/testable, so below just shows an approach (see also comments inline)
struct CompanyView: View {
#ObservedObject var dataManager: DataManager = DataManager.shared
#State var selTipoAzienda: Int
init() {
// set up initial value from persistent data manager
_selTipoAzienda = State(initialValue: self.dataManager.company.tipoAziendaList.firstIndex(of: self.dataManager.company.tipoAzienda) ?? 0)
}
var body: some View {
// inline proxy binding to intercept Picker changes
let boundSelTipoAzienda = Binding(get: { self.selTipoAzienda }, set: {
self.selTipoAzienda = $0
// store selected value into data manager
self.dataManager.cambiaTipoAzienda(tipoAzienda: self.dataManager.company.tipoAziendaList[$0])
})
return VStack {
companyPhoto
Text("Company view")
Form {
Picker(selection: boundSelTipoAzienda, label: Text("Tipo Azienda")) {
ForEach(0 ..< self.dataManager.company.tipoAziendaList.count) {
Text(self.dataManager.company.tipoAziendaList[$0])
}
}
}
Button(action: {self.dataManager.cambiaTipoAzienda(tipoAzienda: self.dataManager.company.tipoAziendaList[self.selTipoAzienda]) }) {
Image(systemName: "info.circle.fill")
.font(Font.system(size: 28))
.padding(.horizontal, 16)
}
}
}
}