How to create a function for the button to print the data typed in texfields in the console - swift

struct ContentView: View {
#State private var name : String = ""
var body: some View {
NavigationView{
Form{
Button(action: {
print(Textfield)
}) {
Text("Salvar")
}
I need to print the result typed at Textfield.

You are confusing the view, TextField, which allows you to input text with the variable which stores the result of the input. You can't print the view into which you type, but you can print the value of the variable. Therefore:
struct ContentView: View {
// name holds the value
#State private var name : String = ""
var body: some View {
NavigationView{
Form{
// TextField lets you change the value
TextField("Name", text: $name)
Button(action: {
// prints the value to the console
print(name)
}) {
Text("Salvar")
}
}
}
}
}

Related

Passing a state variable to parent view

I have the following code:
struct BookView: View {
#State var title = ""
#State var author = ""
var body: some View {
TextField("Title", text: $title)
TextField("Author", text: $author)
}
}
struct MainView: View {
#State private var presentNewBook: Bool = false
var body: some View {
NavigationView {
// ... some button that toggles presentNewBook
}.sheet(isPresented: $presentNewBook) {
let view = BookView()
view.toolbar {
ToolbarItem(placement: principal) {
TextField("Title", text: view.$title)
}
}
}
}
}
This compiles but is giving me the following error on runtime:
Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
How do I pass a state variable to some other outside view? I can't use ObservableObject on BookView since that would require me to change it from struct to class
In general, your state should always be owned higher up the view hierarchy. Trying to access the child state from a parent is an anti-pattern.
One option is to use #Bindings to pass the values down to child views:
struct BookView: View {
#Binding var title : String
#Binding var author : String
var body: some View {
TextField("Title", text: $title)
TextField("Author", text: $author)
}
}
struct ContentView: View {
#State private var presentNewBook: Bool = false
#State private var title = ""
#State private var author = ""
var body: some View {
NavigationView {
VStack {
Text("Title: \(title)")
Text("Author: \(author)")
Button("Open") {
presentNewBook = true
}
}
}.sheet(isPresented: $presentNewBook) {
BookView(title: $title, author: $author)
}
}
}
Another possibility is using an ObservableObject:
class BookState : ObservableObject {
#Published var title = ""
#Published var author = ""
}
struct BookView: View {
#ObservedObject var bookState : BookState
var body: some View {
TextField("Title", text: $bookState.title)
TextField("Author", text: $bookState.author)
}
}
struct ContentView: View {
#State private var presentNewBook: Bool = false
#StateObject private var bookState = BookState()
var body: some View {
NavigationView {
VStack {
Text("Title: \(bookState.title)")
Text("Author: \(bookState.author)")
Button("Open") {
presentNewBook = true
}
}
}.sheet(isPresented: $presentNewBook) {
BookView(bookState: bookState)
}
}
}
I've altered your example views a bit because to me the structure was unclear, but the concept of owning the state at the parent level is the important element.
You can also pass a state variable among views as such:
let view = BookView(title: "foobar")
view.toolbar {
ToolbarItem(placement: principal) {
TextField("Title", text: view.$title)
}
}
Then, inside of BookView:
#State var title: String
init(title: String) {
_title = State(initialValue: title)
}
Source: How could I initialize the #State variable in the init function in SwiftUI?

SwiftUI SceneDelegate - contentView Missing argument for parameter 'index' in call

I am trying to create a list using ForEach and NavigationLink of an array of data.
I believe my code (see the end of the post) is correct but my build fails due to
"Missing argument for parameter 'index' in call" and takes me to SceneDelegate.swift a place I haven't had to venture before.
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
I can get the code to run if I amend to;
let contentView = ContentView(habits: HabitsList(), index: 1)
but then all my links hold the same data, which makes sense since I am naming the index position.
I have tried, index: self.index (which is what I am using in my NavigationLink) and get a different error message - Cannot convert value of type '(Any) -> Int' to expected argument type 'Int'
Below are snippets of my code for reference;
struct HabitItem: Identifiable, Codable {
let id = UUID()
let name: String
let description: String
let amount: Int
}
class HabitsList: ObservableObject {
#Published var items = [HabitItem]()
}
struct ContentView: View {
#ObservedObject var habits = HabitsList()
#State private var showingAddHabit = false
var index: Int
var body: some View {
NavigationView {
List {
ForEach(habits.items) { item in
NavigationLink(destination: HabitDetail(habits: self.habits, index: self.index)) {
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.description)
}
}
}
}
}
}
}
}
struct HabitDetail: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var habits: HabitsList
var index: Int
var body: some View {
NavigationView {
Form {
Text(self.habits.items[index].name)
}
}
}
}
You probably don't need to pass the whole ObservedObject to the HabitDetail.
Passing just a HabitItem should be enough:
struct HabitDetail: View {
#Environment(\.presentationMode) var presentationMode
let item: HabitItem
var body: some View {
// remove `NavigationView` form the detail view
Form {
Text(item.name)
}
}
}
Then you can modify your ContentView:
struct ContentView: View {
#ObservedObject var habits = HabitsList()
#State private var showingAddHabit = false
var body: some View {
NavigationView {
List {
// for every item in habits create a `linkView`
ForEach(habits.items, id:\.id) { item in
self.linkView(item: item)
}
}
}
}
// extract to another function for clarity
func linkView(item: HabitItem) -> some View {
// pass just a `HabitItem` to the `HabitDetail`
NavigationLink(destination: HabitDetail(item: item)) {
HStack {
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.description)
}
}
}
}
}

How to pass a string to a child view?

I want to pass the text in the textBox to the child view and create a scrollable Button there. As for the output status, we hope that 'a ~ c' are arranged vertically and that each is a button.
struct ContentView: View {
var textBox = ["a","b","c"]
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0..<textBox.count) { number in
ScrollText(text: self.textBox[number].lowercased())
}
}
}
}
}
struct ScrollText: View {
#Binding var text: String
#State private var flag: Bool = false
var body: some View {
Button(action: {
self.flag.toggle()
}) {
Text(text)
}
}
}
I'm not totally clear what the problem is, or what you want, but I solved some compiler errors in your code, and it's showing three buttons as expected:
struct ContentView : View {
var textBox = ["a","b","c"]
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false){
ForEach(textBox, id: \.self) { letter in
ScrollText(text: letter)
}
}
}
}
}
struct ScrollText: View {
var text: String
#State private var flag: Bool = false
var body: some View {
Button(action: {
self.flag.toggle()
}, label: {
Text(text)
})
}
}
Your question was how to pass a string, so you don't need #Binding for that. Just pass a string :)
If you're going to keep ScrollText untouched the here is possible modifications in ContentView which uses it
struct ContentView: View {
#State private var textBox = ["a","b","c"] // < make State, so modifiable
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0..<textBox.count) { number in
ScrollText(text: self.$textBox[number]) // < pass Binding as intended
}
}
}
}
}

How to pass a fieldtext value from view another view swiftui

I put the fieldtext in view called fieldtextmydesine and also put the button in view named login and I called fieldtextmydesin view and login view in contentview how do I print the field text value when I press the login button
So you want to use NavigationView and NavigationLink instead of a button.
struct ContentView: View {
#State var name: String = "Tim"
var body: some View {
NavigationView {
VStack {
TextField("Enter your name", text: $name)
Text("Hello, \(name)!")
NavigationLink(destination: SecondView(name: self.$name)){
Text("LogIn")
}
}
}
}
//Second ContenView
struct SecondView: View {
#Binding var name: String
var body: some View {
Text("Hello \(text)")
}
}
From what I understand from your question, you are trying to pass a value entered into a Text-Field from one View to another. If this is what your asking then this is the best solution.
This snippet can help you:
You can bind property to textfield like this
struct ContentView: View {
#State private var name: String = "Tim"
var body: some View {
VStack {
TextField("Enter your name", text: $name)
Text("Hello, \(name)!")
}
}
}
You can add button and print name on button tap line you need. You can pass name property to another text on tap. Or hide text view and show on tap and another ways
You can display print name either on the console or can display name in an alert. In the below snippet, to fetch name entered in text field on button click requires state variable instead of normal variable. It is created with #State keyword. State parameter manages the state in the View. So whenever there is change in state all the components that are associated with the state will be rendered again.
import SwiftUI
struct LoginUI: View {
#State var textName: String = ""
#State var showAlert = false
var body: some View {
VStack(alignment: .center, spacing: 2.0) {
TextField("Enter your name", text: $textName).padding(10)
Button(action: {
print("Entered name is \(self.textName)")
self.showAlert = true
}, label: {Text("Login")}).padding().background(Color.gray)
}
.padding(5.0)
.alert(isPresented: $showAlert) {
Alert(title: Text("Entered name is"), message: Text("\(self.textName)"))
}
}
}
struct LoginUI_Previews: PreviewProvider {
static var previews: some View {
LoginUI()
}
}

List selection as Set<String> - how to use?

Am playing around with SwiftUI and am obviously not getting it.
Basic example which works and is just displaying the selected name.
struct ContentView: View {
let names = ["Joe", "Jim", "Paul"]
#State var selectedName = Set<String>()
var body: some View {
VStack {
List(names, id: \.self, selection: $selectedName) { name in
Text(name)
}
if !selectedName.isEmpty {
Text(selectedName.first!) // <-- this line
}
}
}
}
What I want is a textfield where that name can be changed. Tried many ways but getting another error every time.
TextField("Name", text: $selectedName)
Gives this error: Cannot convert value of type 'Binding<Set<String>>' to expected argument type 'Binding<String>'
TextField("Name", text: $selectedName.first!)
Cannot force unwrap value of non-optional type 'Binding<((String) throws -> Bool) throws -> String?>'
How would I do this?
You may make a binding by yourself:
TextField("Name", text: Binding<String>(get: {self.selectedName.first!}, set: { _ in}) )
Obviously you can't pass Binding<Set<String>> to Binding<String>. Here gives you an idea or solution to change selectedName variable using TextField:
I added a new variable which is Binding<String>. Then I change the selectedName inside the TextField's onCommit closure.
struct ContentView: View {
let names = ["Joe", "Jim", "Paul"]
#State var selectedName = Set<String>()
#State var textFieldName = ""
var body: some View {
VStack {
List(names, id: \.self, selection: $selectedName) { name in
Text(name)
}
if !selectedName.isEmpty {
Text(selectedName.first!)
}
Text(textFieldName)
TextField("Name", text: $textFieldName, onEditingChanged: { (Bool) in
//onEditing
}) {
//onCommit
self.selectedName.insert(self.textFieldName)
}
}
}
}
Ok, here is my alternate if I'd needed to edit some value of names having in one screen and list and edit field and make them all synchronised and not confuse each other.
Here is full testable module (tested on Xcode 11.2/iOS 13.2). As I tested it for iOS there are API requirement for put List into EditMode to process selection, so this included.
struct TestChangeSelectedItem: View {
#State var names = ["Joe", "Jim", "Paul"] // made modifiable
#State var selectedName: String? = nil // only one can be edited, so single selection
#State var editMode: EditMode = .active // Tested for iOS, so it is needed
var body: some View {
VStack {
List(selection: $selectedName) {
ForEach(names, id: \.self) { name in
Text(name)
}
}
.environment(\.editMode, $editMode) // Tested for iOS, so it is needed
if selectedName != nil {
Divider()
Text(selectedName!) // Left to see updates for selection
editor(for: selectedName!) // Separated to make more clear
}
}
}
private func editor(for selection: String) -> some View {
let index = names.firstIndex(of: selection)!
var editedValue = selection // local to avoid cycling in refresh
return HStack {
Text("New name:")
TextField("Name", text: Binding<String>(get: { editedValue }, set: { editedValue = $0}), onCommit: {
self.names[index] = editedValue
self.selectedName = editedValue
})
}
}
}
struct TestChangeSelectedItem_Previews: PreviewProvider {
static var previews: some View {
TestChangeSelectedItem()
}
}