The following would work except that I'm required to use Binding.
struct ContentView: View {
@State var username = ""
@State var password = ""
@State var tenantID = ""
var body: some View {
VStack {
makeForm(label: "Username: ", placeHolder: "123456", text: $username)
makeForm(label: "Password: ", placeHolder: "abcdefg", text: $password)
//makeForm(label: "Shop ID: ", placeHolder: "Amazon River Branch", text: $tenantID)
HStack {
Text("Shop ID: ")
TextField("Amazon River Branch", text: $tenantID)
.onChange(of: tenantID) { newValue in
tenantID = textFilter(value: newValue)
}
}
}.padding(.horizontal, 40.0)
}
@ViewBuilder
private func makeForm(label: String, placeHolder: String, text: Binding<String>) -> some View {
HStack {
let newText = Binding<String>(
get: { text.wrappedValue },
set: { text.wrappedValue = textFilter(value: $0) }
)
Text(label)
TextField(placeHolder, text: newText)
}
}
func textFilter(value: String) -> String {
let pattern = "[^A-Za-z0-9]+"
return value.replacingOccurrences(of: pattern, with: "", options: [.regularExpression])
}
}
Post
Replies
Boosts
Views
Activity
I think I cannot use Binding. Instead, I think I need to use TextField with .onChange.
The warning will disappear with the following, but the text entry won't be filtered. set: { text.wrappedValue = filter(value: $0) }
Currently, the warning says "Expression of type 'String' is unused."
Currently, what I type is not filtered.
I've figured it out accidentally.
import SwiftUI
struct ContentView: View {
@ObservedObject var monster: Monster
var body: some View {
VStack {
Button {
print(monster.items)
} label: {
Text("Show me your current text strings")
}.padding(.vertical, 10.0)
ForEach($monster.items) { item in
MyTextView(id: item.id, text: item.text).environmentObject(monster)
}.padding(.horizontal, 200.0)
Button {
monster.items.append(Keyword())
} label: {
Text("Add me!")
}.padding(.vertical, 10.0)
}
}
}
class Monster: ObservableObject {
@Published var items = [Keyword]()
}
struct Keyword: Identifiable {
var id = UUID()
var text = String()
}
struct MyTextView: View {
@Binding var id: UUID
@Binding var text: String
@EnvironmentObject var monster: Monster
var body: some View {
VStack {
HStack {
TextField("Enter text", text: $text)
Button {
monster.items.removeAll(where: { $0.id == id} )
} label: {
Text("Delete")
}
}
}
}
}
@Claude31 No, sir.
I guess it's something close to the following.
struct ContentView: View {
@State var textMonsters = [MyTextView]()
var body: some View {
VStack {
Button {
print("Monsters \(textMonsters)")
} label: {
Text("Show me your current text strings")
}.padding(.vertical, 10.0)
ForEach(textMonsters, id: \.self) { textMonster in
textMonster
}.padding(.horizontal, 100.0)
Button {
textMonsters.append(MyTextView(id: UUID(), text: "ABC"))
} label: {
Text("Add me!")
}.padding(.vertical, 10.0)
}
}
}
struct MyTextView: View, Identifiable, Hashable {
var id = UUID()
var text: String
var body: some View {
ZStack {
TextField("Enter some text", text: Binding(get: { text }, set: { _ in
}))
}
}
}
I need to change MyTextView() to MyTextView(text: "Hello")
The following Stack Overflow could be a solution that I've been looking for. https://stackoverflow.com/questions/58842453/swiftui-hstack-with-wrap
Well, I guess I could do the following.
import SwiftUI
struct ContentView: View {
@State private var names: [String] = ["Jim Thorton", "Susan Murphy", "Tom O'Donnell", "Nancy Smith"]
var body: some View {
HStack {
ForEach($names, id: \.self) { $name in
TextField("", text: $name)
.fixedSize()
}
}
}
}
I got it thanks to Claude31.
import SwiftUI
struct ContentView: View {
@ObservedObject var monster: Monster
var body: some View {
VStack {
Button {
monster.items.append(Keyword())
} label: {
Text("Add me!")
}.padding(.vertical, 10.0)
ForEach(monster.items) { item in
KeywordRow(id: item.id).environmentObject(monster)
}
}
}
}
struct Keyword: Identifiable {
let id = UUID()
}
struct KeywordRow: View {
@State var id = UUID()
@EnvironmentObject var monster: Monster
var body: some View {
VStack {
HStack {
Text("ID: \(id)")
Button {
monster.items.removeAll(where: { $0.id == id} )
} label: {
Text("Delete")
}
}
}
}
}
I guess the user State object in ContentView comes with default values.
import SwiftUI
struct ContentView: View {
@EnvironmentObject var gameSettings: GameSettings
var body: some View {
GeometryReader { geo in
ZStack {
HStack(spacing: 0.0) {
RightView().environmentObject(gameSettings)
.frame(width: geo.size.width / 2.0, height: geo.size.height)
Spacer()
}
VStack {
HStack {
Spacer()
Button {
print("\(gameSettings.score)")
} label: {
Text("Print")
.font(.largeTitle)
}.padding(.trailing, 40.0)
}
Spacer()
}
}
}
}
}
I guess I've solved a problem on my own again.
import SwiftUI
struct ContentView: View {
@StateObject var monster = MonsterObservable.shared
var body: some View {
GeometryReader { geo in
ZStack {
HStack(spacing: 0.0) {
RightView()
.frame(width: geo.size.width, height: geo.size.height)
}
ShowDialogView(isShowing: $monster.showDialog) {
}
.frame(width: 500, height: 600, alignment: .center)
.cornerRadius(10.0)
}
}
}
}
struct ShowDialogView<Content: View>: View {
@Binding var isShowing: Bool
@ViewBuilder let content: () -> Content
var body: some View {
Group {
if isShowing {
ZStack {
Color.brown
VStack {
Spacer()
Button {
isShowing = false
} label: {
Text("Close me")
.font(.largeTitle)
}.padding([.top, .bottom], 100.0)
}
}
}
}
}
}
class MonsterObservable: ObservableObject {
static let shared = MonsterObservable()
@Published var showDialog = false
}
class GameScore: ObservableObject {
static let shared = GameScore()
@Published var score = 0
}
struct RightView: View {
@ObservedObject var monster = MonsterObservable.shared
var body: some View {
ZStack {
Color.red
Button {
monster.showDialog = true
} label: {
Text("Change")
.font(.largeTitle)
}
}
}
}