Unsure if you meant the command for undo like shaking the phone or just text, but if you are just wanting to react to changes on the document via text, you could just add an observer to the text variable in the *Document.swift file.
var text: String {
didSet {
print("Document changed")
}
}
Anytime the text changes in the document, the didSet observer will be called. I tried messing around with some custom Bindings, but it wasn't as straightforward/elegant.
Post
Replies
Boosts
Views
Activity
I tested this out on my iPad with playgrounds 3.4, and for me, it also didn't work except that it said that there was a problem and check the code for mistakes. I added resizable and frame modifiers to the Image view which allowed it to load inside the app. For the image, I just added it using the blue plus in the upper right corner of the app.
Image(uiImage: #imageLiteral(resourceName: "Photo.png"))
.resizable()
.frame(width: 200, height: 200)
.clipShape(Circle())
Not the same issue, but it does appear that 3.4 is having some issues. The ForEach loop isn't working as expected, it will iterate through the data, but it doesn't render correctly whatever is in the brackets.
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
/*Text("TEST")*/
ForEach((1...5), id: \.self) { index in
Text("TEST TEST")
}
}
.foregroundColor(Color.green)
.background(Color.red)
}
}
PlaygroundPage.current.setLiveView(ContentView())
When run in the playground, the Text view inside for ForEach loop does not display the text, but the background color does show that the view is there, but the text just doesn't get displayed. When the Text view outside of the ForEach loop is uncommented and ran, an alert appears stating "There was a problem running this playground. Please check your code and try again." I copied over the code to Xcode, and it ran as expected. I'm on the new iPadOS developer beta and playgrounds 3.4.
I copied your code it and confirmed that it didn't run in Playgrounds 3.4. If you take out
Color.yellow.edgesIgnoringSafeArea([.horizontal, .bottom])
your code runs as expected. What are you trying to make yellow?
I think there is an issue currently where Playgrounds 3.4 GeometryReader isn't registering the screen size for the live view. I added the the frame modifier to the GeometryReader view and this allows it to run in Playgrounds 3.4 and not run into the Abort() issue. I tried to add the frame modifier to ContentView(), but it resulted in the Abort() function being called. Unsure if there is a place to see the logs for Playgrounds to see what causes Abort() to be called.
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
Color.blue.frame(height: geometry.size.height * 0.5)
}.frame(width:500, height: 500)
}
@richiwalt,
I answered a different question on the forms and I think the issue is the same. After the update to 3.4, I don't think the screen size of the live view is being registered currently inside the ContentView (or any view) which is leading to unexpected behavior for views that need that information.
struct ContentView: View {
var emojis = "🐿 🐝🦋🐌🐞🐜🐛🦒🐘🦛🦓🦏🐪"
var body: some View {
VStack {
ScrollView(.horizontal) {
HStack {
ForEach( emojis.map { String($0) }, id: \.self ) { emoji in
Text(emoji).font(Font.system(size: 45))
}
}
.padding(.horizontal)
}
Color.yellow.edgesIgnoringSafeArea([.horizontal, .bottom])
}
.frame(width: 500, height: 500)
.background(Color.white)
}
}
If you add the frame modifier it seems to make it work in the meantime. It scrolls left and right without crashing.
This was able to be fixed via added the frame modifier to the list view.
List {
ForEach( fruitList ) { fruit in
HStack {
Text("\(fruit.emoji)")
Text("\(fruit.name)")
}
.font(.title)
}
}.frame(width: 500, height: 500)
I would assume based on the other question with Abort(), that Abort() is called if it is about to crash, and it seems that the view isn't getting the bounds of the screen in the live view, so it doesn't know how/where to render the view.
That is interesting @Yoko_Ono. I made the project for MacOS, and didSet does get called when I hit CMD + Z (on both undo and redo). Although, it seems very inconsistent what it undos. Either it will just do one work or everything that was done in the document.
@Enevold,
Your code does work and CTRLIP would contain the text of whatever the user entered in the textfield. Based on what information you have given, you seem to be treating CTRLIP as a global variable. You mention that you have this line in another struct:
let servIp = "$CTRLIP"
If this line is in another struct, "$CTRLIP" is just a string nothing to do with the State variable in the ServerIPInput view. What OOPer was referring to was posting that struct that contains the servIp variable to see why it isn't connected.
It is unclear to see how your struct that contains servIp is getting the value from the ServerIPInput view. If ServerIPInput is a child view of this struct that contains servIp, you can turn CTRLIP into a Binding variable which will create the connection for data to transfer back to the parent view, but the parent view would need the State.
@Enevold,
I looked at what you have, and it did confirm what I was thinking that you are trying to use global variables. I would suggest refactoring your code and look into some tutorials on how SwiftUI handles the movement of data. Below I have modified your program some:
struct ContentView: View {
@State private var servIp: String = "192.168.0.196"
let port: Int = 8000
let cmd: String = "xmlcommand"
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Enter Control IP")
TextField("Enter Control IP...", text: $servIp)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
Button(action: {
sendHttpPostDesktops()
}, label: {
Text("Desktops")
}).buttonStyle(PlainButtonStyle())
}
}
func sendHttpPostDesktops() {
print("servIP = \(servIp)")
}
}
I removed your ServerIPInput struct because I didn't see where you called this view ContentView. In the example above, servIp will be whatever the user enters into the textfield; otherwise, it will be keep the initial value of 192.168.0.196. If you want another struct view, you should look into how to bind to a state variable to create a connection for passing data between views. My final suggestion would be looking into making an object that contains all the server logic. For example, you can make an observable object that contains the sendHttpPostDesktops function. This way, it separates your view and data logic.
@richiwalt,
Most of the current issues can be fixed with adding the frame modifier just inside the body view which fixes GeometryReader and ForEach. It is a temporary fix to get the program running in the app again. In the next update to Playgrounds (or potentially a mini update) will see this fixed (hopefully).
I do not know if Apple engineers monitor this form. Yes, there is a place where you can submit feedback to Apple. On all the latest versions of MacOS, iOS, and iPadOS, there is the Feedback app (Feedback Assistant on MacOS). Here you can submit errors, bugs, or feedback to Apple. I would fully suggestion entering feedback to send to Apple, so they can get a better understanding of what is occurring.
I don't know if it's a bug or just undefined behavior. It looks like it always displays a blank toolbar when isFavorite gets updated. My guess is that SwiftUI don't know which one should be on top (or it renders the ContentView toolbar, but it doesn't show the text because it doesn't exist in DetailView?). If you comment out the Image(systemName: isFavorite ? "star.fill" : "star"), the ContentView doesn't get rendered again, so it doesn't change the toolbar, but it seems that when DetailView propagates an update to ContentView is clashes over what ToolbarItem(placement: .bottomBar) to use.
I tried a couple different solutions, conditional in the ToolbarItem, making an observable object, settings views with onAppear and onDisappear, but non of them resulted in a work around for this problem.
Either it it s bug, or this type of behavior isn't supposed to happen in SwiftUI. Do you have an example of an App that is dynamically changing the toolbar based on the view in SwiftUI?
@Tact,
There wasn't much provided except for a few pieces of code, so it is hard to determine the exact flow of your application, so I took some liberty in piecing together what you might have. I have also never tried to implement this style of application, so I decided to try my best to solve it; learned some new things! Below is what I got working:
class Store: ObservableObject {
@Published var backgroundColor: Color
init() {
self.backgroundColor = .green
}
}
struct ChangeBackgroundColor: View {
@Binding var backgroundColor: Color
var body: some View {
ZStack {
VStack {
Button(action: { self.backgroundColor = .black }, label: { Text("Black") })
Button(action: { self.backgroundColor = .green }, label: { Text("Green") })
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(backgroundColor).edgesIgnoringSafeArea(.all)
}
}
struct ContentView: View {
@ObservedObject var store: Store = Store()
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ChangeBackgroundColor(backgroundColor: $store.backgroundColor)) {
Text("Pick Background Color")
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(store.backgroundColor).edgesIgnoringSafeArea(.all)
}
}
}
I ran into the issue that others have encountered when trying to set the background color that involves the NavigationView. It appears that the the underline View either doesn't have/use the dimensions of the screen, so either using GeometryReader or .infinity should get a desired effect of turning the whole background color via just SwiftUI. Hopefully this will get you going and understand the relationship on how to pass values around different views.
If you don't plan on using NavigationView or some of the other views that have issues with .background, you can easily use Color() example you provided.
I think this might be fixed in Xcode 12.3 Beta. I ran the code in the live preview and in the simulator, and I am able to hit the close button on the upper right corner to close the ColorPicker and have the selected color outside that view.
The first question I have is about that Binding. Since this is the ContentView (presumably the first view of your app), what State is it being bound to? I didn't know, so based on the fact that it was a bind, I rewrote it to include another view (compacted the code to make it smaller):
struct OtherView: View {
@Binding var x1: Int
@State var x2: Int = 0
var body: some View {
VStack {
Button(action: {
withAnimation { x1 += 1; x2 += 2 }
}, label: { Text("Increment").padding() })
Text("Total: \(total)")
}
}
var total: Int { print("\(x1) + \(x2) = \(x1 + x2)"); return x1 + x2 }
}
struct ContentView: View {
@State private var value: Int = 0
var body: some View {
OtherView(x1: $value)
}
}
With this, it doesn't update the view twice, and only prints, 1 + 2 = 3.
In regards to your question about using a Binding and a State, this is possible (as shown above), but I would need to see your other view that the Binding is being connected to. If this doesn't answer your question, can you please expand/explain why you are using a Binding without binding it to something.