Missing arguments for parameters even when initialised - ios

I am trying to call a detailed view of an item when an item is tapped. In this case, the item is a pair of trousers in the MarketplaceTrouserView. When I call TrouserDetailView, I get an error. This must be to do with initialising but I've repeatedly tried this and failed. What could be the solution?
MarketplaceTrouserView:
import SwiftUI
struct MarketplaceTrouserView: View {
#StateObject var MarketplaceModel = MarketplaceViewModel()
#State private var selectedMarketplaceFilter: MarketplaceFilterViewModel = .trouser
#State var showDetailTrouser = false
#State var selectedTrouser : Trouser!
#EnvironmentObject var sharedData: SharedDataModel
var body: some View {
var columns = Array(repeating: GridItem(.flexible()), count: 2)
ZStack{
VStack{
HStack {
Text("Find Trousers To Buy")
}
}
}
if MarketplaceModel.trousers.isEmpty{
ProgressView()
}
else{
ScrollView {
LazyVGrid(columns: Array(repeating: GridItem(.flexible(),spacing: 10), count: 2),spacing: 20){
ForEach(MarketplaceModel.filteredTrouser){trouser in
// Trouser items in grid view
TrouserView(trouserData: trouser)
.onTapGesture {
withAnimation {
selectedTrouser = trouser
showDetailTrouser.toggle()
}
}
}
}
}
}
}
if selectedTrouser != nil && showDetailTrouser{
TrouserDetailView(/*Here is the error asking for trouserData & showDetailTrouser*/)
}
}
}
}
TrouserDetailView:
import SwiftUI
import SDWebImageSwiftUI
struct TrouserDetailView: View {
#State var trouserData : Trouser
#State var showDetailTrouser: Bool
#EnvironmentObject var sharedData: SharedDataModel
#EnvironmentObject var marketplaceData: MarketplaceViewModel
var body: some View {
ScrollView {
VStack{
HStack {
Button(action: {
withAnimation(.easeOut){showDetailTrouser.toggle()}
}) {
Image(systemName: "arrow.backward.circle.fill")
}
Text(trouserData.trouser_name)
}
VStack {
WebImage(url: URL(string: trouserData.trouser_image))
}
}
}
}
Trouser Model:
import SwiftUI
import FirebaseFirestoreSwift
import Firebase
struct Trouser: Identifiable, Codable {
#DocumentID var id: String?
var trouser_name: String = ""
var trouser_image: String = ""
}
The error is when i call TrouserDetailView (as marked in the code)
After applying changes, my MainPage view has errors in the initialiser.
MainPage
struct MainPage: View {
#StateObject var appModel: AppViewModel = .init()
#StateObject var sharedData: SharedDataModel = SharedDataModel()
#State var shirtData : Shirt
#State var showDetailShirt: Bool
#State var trouserData : Trouser
#State var showDetailTrouser: Bool
#Namespace var animation
init () {
#State var shirtData = Shirt()
#State var showDetailShirt = false
#State var trouserData = Trouser()
#State var showDetailTrouser = false
UITabBar.appearance().isHidden = true
} //ERROR HERE: 'Return from initializer without initializing all stored properties'
var body: some View {
VStack {
TabView(selection: $appModel.currentTab) {
Marketplace()
.environmentObject(sharedData)
.tag(Tab.Market)
.setUpTab()
Home()
.environmentObject(sharedData)
.tag(Tab.Home)
.setUpTab()
}

By the logic of provided code, state is not needed for the first one and second should be replaced with binding, ie.
struct TrouserDetailView: View {
var trouserData : Trouser
#Binding var showDetailTrouser: Bool
// ...
and call
if selectedTrouser != nil && showDetailTrouser {
TrouserDetailView(trouserData: selectedTrouser,
showDetailTrouser: $showDetailTrouser)
}

#State var trouserData : Business
#State var showDetailTrouser: Bool
These variables you must initialize the value locally or switch to #Binding to bind the data with the parent variables.

Related

Missing parameters in Call in TabView

I have code that makes my filter bar disappear when user has tapped on a product (a shirt or pair of trousers). This all takes place in the Marketplace tab.
I think the code will work once the bug is fixed however I cannot get rid of the buh about missing arguments.
The code for marketplace is:
#Binding var shirtData : Shirt!
#Binding var showDetailShirt: Bool
#Binding var trouserData : Trouser!
#Binding var showDetailTrouser: Bool
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 10){
if !showDetailShirt && !showDetailTrouser {
marketplaceFilterBar
}
if selectedMarketplaceFilter == .shirt {
MarketplaceShirtView()
}
if selectedMarketplaceFilter == .trouser {
MarketplaceTrouserView()
}
}
}
Here is code for the TabView
#StateObject var appModel: AppViewModel = .init()
#StateObject var sharedData: SharedDataModel = SharedDataModel()
#Binding var shirtData : Shirt!
#Binding var showDetailShirt: Bool
#Binding var trouserData : Trouser!
#Binding var showDetailTrouser: Bool
// Animation Namespace...
#Namespace var animation
// Hiding Tab Bar...
init(){
UITabBar.appearance().isHidden = true
}
var body: some View {
VStack(spacing: 0){
// Tab View...
TabView(selection: $appModel.currentTab) {
Marketplace(animation: _animation)
.environmentObject(sharedData)
.tag(Tab.Market)
.setUpTab()
Home()
.environmentObject(sharedData)
.tag(Tab.Home)
.setUpTab()
}
On the Marketplace line in the TabView I am getting the error:
Missing arguments for parameters 'shirtData', 'showDetailShirt', 'trouserData', 'showDetailTrouser' in call
UPDATED:
On my MarketplaceShirtView I have the following code:
#StateObject var MarketplaceModel = MarketplaceViewModel()
#State private var selectedMarketplaceFilter: MarketplaceFilterViewModel = .shirt
#Namespace var animation : Namespace.ID
#State var showDetailShirt = false
#State var selectedShirt : Shirt!
// Shared Data...
#EnvironmentObject var sharedData: SharedDataModel
When I use an if statement to determine whether to display the Detail view I get the error: Cannot convert value of type 'Binding<Shirt?>’ to expected argument type 'Binding'
Here is the code:
if selectedShirt != nil && showDetailShirt{
ShirtDetailView(shirtData: $selectedShirt, showDetailShirt: $showDetailShirt,animation: _animation)
}
According to the code you posted MarcetPlace should be initialized like this:
Marketplace(shirtData: $shirtData, showDetailShirt: $showDetailShirt, trouserData: $trouserData, showDetailTrouser: $showDetailTrouser)
Edit:
Well you got a custom initializer and properties without default values there so probably you have to initialize those too.
E.g.
init(shirtData: Binding<Shirt>, .....
Edit 2:
To be more precise, in your TabView:
// Hiding Tab Bar...
init(shirtData: Binding<Shirt?>, showDetailShirt: Binding<Bool>, trouserData: Binding<Trouser?>, showDetailTrouser: Binding<Bool>){
self._shirtData = shirtData
self._showDetailShirt = showDetailShirt
self._trouserData = trouserData
self._showDetailTrouser = showDetailTrouser
UITabBar.appearance().isHidden = true
}
I don´t think you need those implicit unwrappings so instead of #Binding var shirtData : Shirt! use #Binding var shirtData : Shirt and in the initializer init(shirtData: Binding<Shirt?>, would become init(shirtData: Binding<Shirt>,

How to use #Binding when view is creating using function in SwiftUI?

I created view using Function and which i am calling from another view (AbcView), I want to perform normal #Binding with that, but not sure how to pass value and create #Binding in function.
In Below code I want to pass selectedPassengerId from AbcView to function topSheetClassViews and perform #Binding whenever value passengerIds in SelectedTitleView is updating so that I can get updated value in AbcView.
import SwiftUI
struct AbcView: View {
#StateObject var abcViewModel: AbcViewModel
#State private var selectedPassengerId: Int?
init(accessibiltyID: String, abcViewModel: AbcViewModel) {
self._abcViewModel = StateObject(wrappedValue: abcViewModel)
}
var body: some View {
VStack(spacing: 0) {
// Some Design
}
.overlay(
TopView((accessibilityID: accessibilityID, content: topSheetClassViews(abcViewModel: abcViewModel), selectedRowID: $selectedPassengerId, rowHeight: $rowHeight),, alignment: .top
)
)
}
}
func topSheetClassViews(abcViewModel: AbcViewModel) -> [AnyView] {
var views: [AnyView] = []
for passenger in 0..<abcViewModel.Passengers.count {
views.append(TopSheetPassengerInfoView(abcViewModel: abcViewModel, index: passenger).convertToAnyView())
}
return views
}
struct SelectedTitleView: View {
#ObservedObject var abcViewModel: AbcViewModel
var passengerIds: Int
var body: some View {
VStack(alignment: .trailing) {
Text("passengerIds \(passengerIds)") // here getting correct id which I want to pass to AbcView
Text(abcViewModel.passengerTitle(passengerId: passengerIds))
}
}
}
struct TopSheetPassengerInfoView: View {
#ObservedObject var abcViewModel: AbcViewModel
var index: Int
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text(abcViewModel.passengers[index].fullName ?? "")
SelectedTitleView(abcViewModel: abcViewModel, passengerIds: Int(abcViewModel.Passengers[index].passengerId ?? "") ?? 0)
}
}
}
Just put it through everywhere you need to pass it, like
here
func topSheetClassViews(abcViewModel: AbcViewModel, selection: Binding<Int?>) -> [AnyView] {
here
views.append(TopSheetPassengerInfoView(abcViewModel: abcViewModel, selectedID: selection, index: passenger).convertToAnyView())
here
struct TopSheetPassengerInfoView: View {
#ObservedObject var abcViewModel: AbcViewModel
#Binding var selectedID: Int?
and so on

SwiftUI: detecting when #State variable has change in their #Binding

I'm trying to figure how can change the value from different View. Here is the implementation of my main view:
import SwiftUI
import Combine
struct ContentView: View {
#State private var isPresented: Bool = false
#State private var textToProces = "" {
didSet{
print("text: oldValue=\(oldValue) newValue=\(textToProces)")
}
}
var body: some View {
ZStack{
VStack{
Button("Show Alert"){
self.isPresented = true
}.background(Color.blue)
}
ItemsAlertView(isShown: $isPresented, textToProcess: $textToProces)
}
}
}
On this view I'm trying to change the textToProces variable:
struct AnotherView: View {
#Binding var isShown: Bool
#Binding var textToProcess: String
var title: String = "Add Item"
let screenSize = UIScreen.main.bounds
var body: some View {
VStack {
Button(action: {
self.textToProcess = "New text"
self.isShown = false
}, label: {
Text("dissmis")
})
Text(self.textToProcess)
}
.background(Color.red)
.offset(y: isShown ? 0 : screenSize.height)
}
}
When I change the value on this line self.textToProcess = "New text" the textToProcess in the main view never gets the notification of the change. What I can I do to get the notification of the change in the main view any of you knows?
I'll really appreciate your help
You have to use the onChange modifier to track changes to textToProces.
import SwiftUI
import Combine
struct ContentView: View {
#State private var isPresented: Bool = false
#State private var textToProces = ""
var body: some View {
ZStack{
VStack{
Button("Show Alert"){
self.isPresented = true
}.background(Color.blue)
}
ItemsAlertView(isShown: $isPresented, textToProcess: $textToProces)
}
.onChange(of: textToProces) { value in
print("text: \(value)")
}
}
}

Multiple SwiftUI views sharing data members

Here's my situation.... I have a view called SwiftUIView() - it contains a picker and a textbox
You can see it here:
import SwiftUI
import Combine
class section_array: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
var first_value = 0 { willSet { objectWillChange.send() } }
var second_value = 0 { willSet { objectWillChange.send() } }}
struct SwiftUIView: View {
#Binding var master_sections: [ContentView.my_Section]
#ObservedObject private var first_value: section_array = section_array()
// #Binding var current_section: ContentView.my_Section
#State private var comment: String = String()
var body: some View {
VStack {
Picker(selection: self.$first_value.first_value , label: Text("Subsection")) {
ForEach(0 ..< self.master_sections.count) {
Text(self.master_sections[$0].name)
}}
TextField("Enter your comment.", text: $comment)
}
}
}
Attempting to utlizie this view here:
ForEach(master_subsections) { result in
SwiftUIView(master_sections: self.$subsections)
}
Causes the right amount of SwiftUIViews to appear, however, they all seem to share the same $comment and $first_value, these are private fields - I need them unique to each occurance of the SwiftUIView().

So I built an app that's suppose to get the words per minute. How do you start a timer that's not in ContentView with SwithUI

I want to start a timer of 60 seconds to test how many words a user can type within that minute. I started counting the characters within the TextField. But Now I need to decrement a timer so I can do the math and print out the answer to the user. I can't seem to figure out how to use the timer when it's not in the Content View struct though. Can I do that?
import SwiftUI
struct ContentView: View {
#State var userInput = ""
#State var modalview = false
#State var getstarted = false
#EnvironmentObject var timerHolder : TimerHolder
var body: some View {
ZStack() {
modalView(modalview: $modalview, userInput: userInput)
}.sheet(isPresented: $modalview) {
modalView(modalview: self.$modalview)
}
}
}
struct modalView : View {
#ObservedObject var durationTimer = TimerHolder()
#Binding var modalview : Bool
#State var userInput: String = ""
var body: some View {
VStack{
Button(action: {
self.modalview = true
}) {
TextField("Get Started", text:$userInput)
.background(Color.gray)
.foregroundColor(.white)
.frame(width: 300, height: 250)
.cornerRadius(20)
Text("\(userInput.count)")
Text("\(durationTimer.count) Seconds")
}
}
}
}
class TimerHolder : ObservableObject {
var timer : Timer!
#Published var count = 0
func start() {
self.timer?.invalidate()
self.count = 0
self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
_ in
self.count += 1
print(self.count)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
}
The simples one, as you hold it as property, is to start in .onAppear... (supposing, of course, that you pass it in ContentView().environmentObject(TimerHolder()) on ContentView creation)
struct ContentView: View {
#State var userInput = ""
#State var modalview = false
#State var getstarted = false
#EnvironmentObject var timerHolder : TimerHolder
var body: some View {
ZStack() {
modalView(modalview: $modalview, userInput: userInput)
}.sheet(isPresented: $modalview) {
modalView(modalview: self.$modalview)
}
.onAppear {
self.timerHolder.start()
}
}
}

Resources