Why State update doesn't work

| /

Recently, some personal apps have begun to try to use SwiftUI.

Although some simple layouts, SwiftUI is indeed very efficient in development. But in actual development, many problems will be encountered.

  1. The development concept is different from UIKit, which leads to unaccustomed.
  2. Some components only have UIKit and need to be encapsulated.
  3. There will be compatibility issues with different versions of Xcode and operating systems.
  4. Sometimes some writing methods lead to actual effects that do not meet expectations.
  5. And so on.

I have encountered a situation where the State change does not take effect, and it is also possible that my usage is not completely correct.

Briefly describe my appeal:

  1. A pop-up window is completed by another component, which is actually a UIKit external component. For the convenience of description, I will simply write an example here.
  2. There are 2 buttons on the page, which will determine the data in the pop-up window.
  3. The pop-up window is displayed according to the incoming data.

The expected effect is similar to the following:

It looks simple, doesn’t it, but the actual effect is this:

Strictly speaking, the phenomenon is: the first pop-up window does not pass the modified value, but the default value, and the second and subsequent times, it can be passed correctly.

Here is the sample code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import Foundation
import SwiftUI

struct TestView: View {
var showNum: Int
var body: some View {
VStack {
Text("\(showNum)")
}
}
}

struct EntryView: View {
@State private var testNumber = 0
@State private var showNumber = false
}

var body: some View {
NavigationView {
VStack {
HStack {
Button(action: {
testNumber = 1
self.showNumber.toggle()
}) {
Text("change 1")
}
.qingButtonStyle()

// Text("\(testNumber)")

Button(action: {
testNumber = 2
self.showNumber.toggle()
}) {
Text("change 2")
}
.qingButtonStyle()
}
.sheet(isPresented: $showNumber) {
TestView(showNum: testNumber)
}
}
}.navigationTitle("Choose Photo")
}
}

Looks fine doesn’t it? But the actual effect is very unexpected.

After troubleshooting and trying for a while, I found another fact. Pay attention to line 30 or so of the above code, I leave a comment.

If I use a Text to display testNumber directly on the view, the actual effect will match the expectation.

The effect is as follows:

What a shock!

Considering that SwiftUI is declarative programming, the actual coding is just to use structure to express the view structure. The SwiftUI view refresh logic is closely related to the diff logic calculation of the view tree.

When there is no line 30, the diff logic of the view tree cannot determine the difference, resulting in the view not being refreshed in time.

In the actual project, due to subsequent logic adjustments, multiple States are used to handle the display logic, which bypasses this problem.

Similar to the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct EntryView: View {
//@State private var testNumber = 0
@State private var showNumberOne = false
@State private var showNumberTwo = false
}

var body: some View {
NavigationView {
VStack {
HStack {
Button(action: {
// testNumber = 1
self.showNumberOne.toggle()
}) {
Text("change 1")
}
.qingButtonStyle()

// Text("\(testNumber)")

Button(action: {
// testNumber = 2
self.showNumberTwo.toggle()
}) {
Text("change 2")
}
.qingButtonStyle()
}
.sheet(isPresented: $showNumberOne) {
TestView(showNum: 1)
}
.sheet(isPresented: $showNumberTwo) {
TestView(showNum: 2)
}
}
}.navigationTitle("Choose Photo")
}
}

But it feels very inelegant, but because I really haven’t found a better solution, I can only deal with it temporarily.

If the view refresh of SwiftUI does not meet expectations, you can check in this way, that is, whether the update of the State will directly affect the change of the view tree.

We will continue to explore how to handle this situation more gracefully under SwiftUI later.