Just learning Swift and SwiftUI, having fun in Xcode. I have the following custom view defined, but when I try to test it, the information doesn't get updated as I expect. When I use the button defined INSIDE the custom view, the update works as expected. When I use the button defined in the Preview body, it doesn't. The "lines" variable of the custom view appears not to be updated in that case.
I know I'm missing something fundamental here about either view state binding or the preview environment, but I'm stumped. Any ideas?
import SwiftUI
struct ConsoleView: View {
var maxLines : Int = 26
private enum someIDs { case textID}
@State private var numLines : Int = 0
@State var lines = "a\nb\nc\n"
var body: some View {
VStack(alignment: .leading, spacing:0 ) {
ScrollViewReader { proxy in
ScrollView {
Button("Scroll to Bottom") {
withAnimation {
proxy.scrollTo(someIDs.textID, anchor: .bottom)
}
}
Text(lines)
.id(someIDs.textID)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .bottomLeading)
.padding()
.onChange ( of: lines) {
withAnimation {
proxy.scrollTo( someIDs.textID, anchor: .bottom)
}
}
}
Button("Add more") {
writeln("More")
//writeln(response)
}
}
}
}
private func clipIfNeeded () {
if (numLines>=maxLines) {
if let i = lines.firstIndex(of: "\n") {
lines = String(lines.suffix( from: lines.index(after:i)))
}
}
}
func writeln ( _ newText : String) {
print("adding '\(newText)' to lines")
//clipIfNeeded()
write( newText )
write("\n")
numLines += 1
print(lines)
}
func write ( _ newText : String) {
lines += newText
}
}
#Preview {
VStack() {
var myConsole = ConsoleView(lines: "x\ny\nz\n")
myConsole
Button("Add stuff") {
myConsole.writeln("Stuff")
}
}
}
myConsole.writeln("Stuff")
- This calls a method outside the scope of your SwiftUI view which from your implementation mutates the state of your view.
- First You don't want to mutate SwiftUI state outside the context of the view except through a
binding
or an observable object.
@State var lines = "a\nb\nc\n"
- Declare
state
as private to prevent setting it in a memberwise initializer, which can conflict with the storage management that SwiftUI provides:
I recommend you check out some resources that dives into previews and state management in SwiftUI :
-
Previewing your app’s interface in Xcode: This is an excellent starting point to learn about previews.