SwiftUI - "Argument passed to call that takes no arguments"? - ios

I have an issue with the coding for my app, where I want to be able to scan a QR and bring it to the next page through navigation link. Right now I am able to scan a QR code and get a link but that is not a necessary function for me. Below I attached my code and got the issue "Argument passed to call that takes no arguments", any advice or help would be appreciated :)
struct QRCodeScannerExampleView: View {
#State private var isPresentingScanner = false
#State private var scannedCode: String?
var body: some View {
VStack(spacing: 10) {
if let code = scannedCode {
//error below
NavigationLink("Next page", destination: PageThree(scannedCode: code), isActive: .constant(true)).hidden()
}
Button("Scan Code") {
isPresentingScanner = true
}
Text("Scan a QR code to begin")
}
.sheet(isPresented: $isPresentingScanner) {
CodeScannerView(codeTypes: [.qr]) { response in
if case let .success(result) = response {
scannedCode = result.string
isPresentingScanner = false
}
}
}
}
}
Page Three Code
import SwiftUI
struct PageThree: View {
var body: some View {
Text("Hello, World!")
}
}
struct PageThree_Previews: PreviewProvider {
static var previews: some View {
PageThree()
}
}

You forgot property:
struct PageThree: View {
var scannedCode: String = "" // << here !!
var body: some View {
Text("Code: " + scannedCode)
}
}

You create your PageThree View in two ways, One with scannedCode as a parameter, one with no params.
PageThree(scannedCode: code)
PageThree()
Meanwhile, you defined your view with no initialize parameters
struct PageThree: View {
var body: some View {
Text("Hello, World!")
}
}
For your current definition, you only can use PageThree() to create your view. If you want to pass value while initializing, change your view implementation and consistently using one kind of initializing method.
struct PageThree: View {
var scannedCode: String
var body: some View {
Text(scannedCode)
}
}
or
struct PageThree: View {
private var scannedCode: String
init(code: String) {
scannedCode = code
}
var body: some View {
Text(scannedCode)
}
}
This is basic OOP, consider to learn it well before jump-in to development.
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html

Related

Why does my SwiftUI app jump back to the previous view?

I created an iOS app with SwiftUI and Swift. The purpose is as follows:
User enters an address into an input field (AddressInputView)
On submit, the app switches to ResultView and passes the address
Problem: The ResultView is visible for just a split second and then suddenly jumps back to AddressInputView.
Question: Why is the app jumping back to the previous view (Instead of staying in ResultView) and how can the code be adjusted to fix the issue?
My Code:
StartView.swift
struct StartView: View {
var body: some View {
NavigationStack {
AddressInputView()
}
}
}
AddressInputView.swift
enum Destination {
case result
}
struct AddressInputView: View {
#State private var address: String = ""
#State private var experiences: [Experience] = []
#State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
VStack {
TextField("", text: $address, prompt: Text("Search address"))
.onSubmit {
path.append(Destination.result)
}
Button("Submit") {
path.append(Destination.result)
}
.navigationDestination(for: Destination.self, destination: { destination in
switch destination {
case .result:
ResultView(address: $address, experiences: $experiences)
}
})
}
}
}
}
ExperienceModels.swift
struct ExperienceServiceResponse: Codable {
let address: String
let experiences: [Experience]
}
ResultView.swift
struct ResultView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#Binding private var address: String
#Binding private var experiences: [Experience]
init(address: Binding<String>, experiences: Binding<[Experience]>) {
_address = Binding(projectedValue: address)
_experiences = Binding(projectedValue: experiences)
}
var body: some View {
NavigationStack {
ScrollView {
VStack(alignment: .leading) {
Text("Results")
ForEach(experiences, id: \.name) { experience in
ResultTile(experience: experience)
}
}
}
}
}
}
You have too many NavigationStacks. This is a container view that should be containing your view hierarchy, not something that is declared at every level. So, amend your start view:
struct StartView: View {
#State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
AddressInputView(path: $path)
}
}
}
AddressInputView should take the path as a binding:
#Binding var path: NavigationPath
And you should remove NavigationStack from the body of that view. Likewise with ResultView. I made these changes in a project using the code you posted and the issue was fixed.
I'm not sure exactly why your view is popping back but you're essentially pushing a navigation view onto a navigation view onto a navigation view, so it's not entirely surprising the system gets confused.

How to pass static values in SwiftUI from one View to another?

I'm new to learning SwiftUI and XCode and am unable to figure out how to pass a variable from view to another. I read on #State and #Binding variables but from what I can tell that is for values that change. I have a static value that I calculate based on the date when the user opens the app.
The variable is the current moon phase and is stored locally in my main ContentView. I want to pass this variable to a second view that's accessed by clicking a NavigationLink.
ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
let currentMoonPhaseArray = calculateMoonPhase()
let moonPhase = currentMoonPhaseArray[0]
NavigationView{
ScrollView(.vertical, showsIndicators:true) {
VStack(spacing:3){
NavigationLink(destination: MoonPhaseView()){
Text("Moon Phase - " + moonPhase)
}
}
}
.frame(maxWidth: .infinity)
.navigationTitle("MySky")
.navigationBarTitleDisplayMode(.inline)
}
}
}
MoonPhaseView.swift
import SwiftUI
struct MoonPhaseView: View {
var body: some View {
HStack{
Text("MoonPhaseView!")
}
}
}
struct MoonPhaseView_Previews: PreviewProvider {
static var previews: some View {
MoonPhaseView()
}
}
My goal is to have the calculated moon phase from ContentView.swift be passed to the MoonPhaseView.swift. I believe that bindings are the correct approach from what I've read, but all binding implementations seem to be for updating views often.
Thanks for any help or pointers!
You haven't shown what the type of moonPhase is, so I'm just going to use String as an example.
struct ContentView: View {
func calculateMoonPhase() -> [String] {
return ["Full","Waxing","Waning"]
}
var body: some View {
let currentMoonPhaseArray = calculateMoonPhase()
let moonPhase = currentMoonPhaseArray[0]
NavigationView{
ScrollView(.vertical, showsIndicators:true) {
VStack(spacing:3){
NavigationLink(destination: MoonPhaseView(phase: moonPhase)){
Text("Moon Phase - " + moonPhase)
}
}
}
.frame(maxWidth: .infinity)
.navigationTitle("MySky")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct MoonPhaseView: View {
var phase : String
var body: some View {
HStack{
Text("MoonPhaseView!")
Text(phase)
}
}
}
struct MoonPhaseView_Previews: PreviewProvider {
static var previews: some View {
MoonPhaseView(phase: "Full")
}
}
Note that every time MoonPhaseView is used, you must provide a phase parameter so that it has a value to fill var phase : String. You could provide a default value, but that doesn't seem like it would do much good here.
Not directly related to your question, but I might suggest that calculating the phase in the body might lead to undesirable results, especially if it's an expensive calculation or it has to contact an API or something. You might want to consider doing this in onAppear and keeping it in a #State variable in your ContentView, or perhaps even using an ObservableObject as a view model and storing the phase there.
You can use "Environment" to pass system-wide settings to views and child views.
For example:
#main
struct TestApp: App {
let moonPhaseValue = "Waxing" // <--- some value
var body: some Scene {
WindowGroup {
ContentView().environment(\.moonPhase, moonPhaseValue) // <--- pass it around
}
}
}
struct ContentView: View {
#Environment(\.moonPhase) var moonPhase // <--- the value again
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: MoonPhaseView()) {
Text("Moon Phase - " + moonPhase)
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct MoonPhaseView: View {
#Environment(\.moonPhase) var moonPhase // <--- the value again
var body: some View {
HStack{
Text("MoonPhaseView is \(moonPhase)")
}
}
}
// create your own EnvironmentKey
struct MoonPhaseKey: EnvironmentKey {
static let defaultValue: String = "Full"
}
// create your own EnvironmentValues
extension EnvironmentValues {
var moonPhase: String {
get { return self[MoonPhaseKey] }
set { self[MoonPhaseKey] = newValue }
}
}

How to notify view that the variable state has been updated from a extracted subview in SwiftUI

I have a view that contain users UsersContentView in this view there is a button which is extracted as a subview: RequestSearchButton(), and under the button there is a Text view which display the result if the user did request to search or no, and it is also extracted as a subview ResultSearchQuery().
struct UsersContentView: View {
var body: some View {
ZStack {
VStack {
RequestSearchButton()
ResultSearchQuery(didUserRequestSearchOrNo: .constant("YES"))
}
}
}
}
struct RequestSearchButton: View {
var body: some View {
Button(action: {
}) {
Text("User requested search")
}
}
}
struct ResultSearchQuery: View {
#Binding var didUserRequestSearchOrNo: String
var body: some View {
Text("Did user request search: \(didUserRequestSearchOrNo)")
}
}
How can I update the #Binding var didUserRequestSearchOrNo: String inside the ResultSearchQuery() When the button RequestSearchButton() is clicked. Its so confusing!
You need to track the State of a variable (which is indicating if a search is active or not) in your parent view, or your ViewModel if you want to extract the Variables. Then you can refer to this variable in enclosed child views like the Search Button or Search Query Results.
In this case a would prefer a Boolean value for the tracking because it's easy to handle and clear in meaning.
struct UsersContentView: View {
#State var requestedSearch = false
var body: some View {
ZStack {
VStack {
RequestSearchButton(requestedSearch: $requestedSearch)
ResultSearchQuery(requestedSearch: $requestedSearch)
}
}
}
}
struct RequestSearchButton: View {
#Binding var requestedSearch: Bool
var body: some View {
Button(action: {
requestedSearch.toggle()
}) {
Text("User requested search")
}
}
}
struct ResultSearchQuery: View {
#Binding var requestedSearch: Bool
var body: some View {
Text("Did user request search: \(requestedSearch.description)")
}
}
Actually I couldn't understand why you used two struct which are connected to eachother, you can do it in one struct and Control with a state var
struct ContentView: View {
var body: some View {
VStack {
RequestSearchButton()
}
}
}
struct RequestSearchButton: View {
#State private var clicked : Bool = false
var body: some View {
Button(action: {
clicked = true
}) {
Text("User requested search")
}
Text("Did user request search: \(clicked == true ? "YES" : "NO")")
}
}
if this is not what you are looking for, could you make a detailed explain.

Using #State resolves into 'self' used before all stored properties are initialized

I ran into an issue when using the #State property.
My ContentView.swift looks like this:
import SwiftUI
struct ContentView: View {
#State var showText: Bool = true
var Mod: Modifier
init() {
Mod = Modifier(showText: $showText) // Throws error -> 'self' used before all stored properties are initialized ('self.Mod' not initialized)
}
var body: some View {
VStack {
if showText == true {
Text("Hello, World!")
}
Mod
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
And my Modifier.swift from which the Modifier view is called has following code:
import SwiftUI
struct Modifier: View {
#Binding var showText: Bool
var body: some View {
VStack {
Button("Hide Text") {
self.showText.toggle()
}
}
}
}
I created this simplified code from my actual project that my problem is easier to understand.
Problem
The problem is that the code in the init function results into an error and I don't know how to resolve it.
What I tried and what I would need
Because this is just a simplified version of my actual code there are some requirements I need to my code:
Mod can't be a computed variable
I somehow need the Modifier view as a variable called Mod in my ContentView
When I remove the #State property and the #Binding property and the $ the code works and results with 0 errors. But I need to use the #State property (which unfortunately results into errors with my code)
Also the button to hide and show the text should work
I would be very thankful if anyone could give me a hint. I really appreciate your help!
I did actually find a way to do this. I'm not sure whether it'll be suitable but here are the details.
The problem was that SwiftUI didn't seem to allow setting the Binding outside of body. So this solution returns a new instance of Modifier
struct Modifier: View {
#Binding var showText: Bool
var body: some View {
VStack {
Button("Hide Text") {
self.showText.toggle()
}
}
}
// this function returns a new instance with the binding
func bind(to binding: Binding<Bool>) -> Self {
return Modifier(showText: binding)
}
}
And the code for ContentView, where we can call this function from within body:
struct ContentView: View {
#State var showText: Bool = true
var Mod: Modifier
init() {
Mod = Modifier(showText: .constant(true)) // .constant() gives a placeholder Binding
}
var body: some View {
return VStack {
if showText == true {
Text("Hello, World!")
}
Mod.bind(to: $showText)
}
}
}
Tested and the text can be hidden/shown. Hope this can help.
Mod = Modifier(showText: _showText.projectedValue)
You can make it let instead of var if you'd like.
Use views inside body context
struct ContentView: View {
#State var showText: Bool = true
var body: some View {
VStack {
if showText == true {
Text("Hello, World!")
}
Modifier(showText: $showText)
}
}
}

How to use Dictionary as #Binding var in SwiftUI

I will need to display a collapsed menu in SwiftUI, it is possible to pass one single bool value as binding var to subviews but got stuck when trying to pass that value from a dictionary.
see code below:
struct MenuView: View {
#EnvironmentObject var data: APIData
#State var menuCollapsed:[String: Bool] = [:]
#State var isMenuCollapsed = false;
// I am able to pass self.$isMenuCollapsed but self.$menuCollapsed[menuItem.name], why?
var body: some View {
if data.isMenuSynced {
List() {
ForEach((data.menuList?.content)!, id: \.name) { menuItem in
TopMenuRow(dataSource: menuItem, isCollapsed: self.$isMenuCollapsed)
.onTapGesture {
if menuItem.isExtendable() {
let isCollapsed = self.menuCollapsed[menuItem.name]
self.menuCollapsed.updateValue(!(isCollapsed ?? false), forKey: menuItem.name)
} else {
print("Go to link:\(menuItem.url)")
}
}
}
}
}else {
Text("Loading...")
}
}
}
in ChildMenu Row:
struct TopMenuRow: View {
var dataSource: MenuItemData
#Binding var isCollapsed: Bool
var body: some View {
ChildView(menuItemData)
if self.isCollapsed {
//display List of child data etc
}
}
}
}
If I use only one single bool as the binding var, the code is running ok, however, if I would like to use a dictionary to store each status of the array, it has the error of something else, see image blow:
if I use the line above, it's fine.
Any idea of how can I fix it?
Thanks
How to use dictionary as a storage of mutable values with State property wrapper?
As mentioned by Asperi, ForEach requires that source of data conforms to RandomAccessCollection. This requirements doesn't apply to State property wrapper!
Let see one of the possible approaches in the next snippet (copy - paste - run)
import SwiftUI
struct ContentView: View {
#State var dict = ["alfa":false, "beta":true, "gamma":false]
var body: some View {
List {
ForEach(Array(dict.keys), id: \.self) { (key) in
HStack {
Text(key)
Spacer()
Text(self.dict[key]?.description ?? "false").onTapGesture {
let v = self.dict[key] ?? false
self.dict[key] = !v
}.foregroundColor(self.dict[key] ?? false ? Color.red: Color.green)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
with the following result

Resources