我有一个非常基本的视图,仅显示TextField:
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
TextField("Enter a string...", text: $viewModel.string)
}
}
Run Code Online (Sandbox Code Playgroud)
的TextField文本绑定到string视图模型上的属性:
class ViewModel: ObservableObject {
@Published var string: String = "" {
didSet {
print("didSet string:", string)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我添加了一个didSet属性观察器,以便在字符串更改时执行自定义操作。对于这个简单的例子,我只在控制台上打印一个字符串。
当我运行此代码并在文本字段中输入字符串“123”时,这是我得到的输出:
didSet string: 1
didSet string: 1
didSet string: 12
didSet string: 12
didSet string: 123
didSet string: 123
Run Code Online (Sandbox Code Playgroud)
为什么?
为什么didSet我输入的每个字符都会调用两次闭包?(我希望每个角色都会调用一次。)
代码有什么问题或者这是预期的行为吗?
binding textfield property-observer swiftui observableobject
在下面的代码中,观察到的对象被更新,但观察它的视图没有更新。知道为什么吗?
代码在屏幕上显示 10 个数字 (0..<10) 和一个按钮。每当按下按钮时,它会随机选择 10 个数字之一并翻转其可见性(可见?隐藏,反之亦然)。
打印语句显示按钮正在更新数字,但视图不会相应更新。我知道更新数组中的值不会改变数组值本身,所以我使用手册objectWillChange.send()调用。我原以为应该触发更新,但屏幕永远不会改变。
任何的想法?我对NumberLine用作类或结构的解决方案感兴趣,或者根本不使用NumberLine类型,而只是在ContentView结构中使用数组变量。
这是代码:
import SwiftUI
struct ContentView: View {
@ObservedObject var numberLine = NumberLine()
var body: some View {
VStack {
HStack {
ForEach(0 ..< numberLine.visible.count) { number in
if self.numberLine.visible[number] {
Text(String(number)).font(.title).padding(5)
}
}
}.padding()
Button(action: {
let index = Int.random(in: 0 ..< self.numberLine.visible.count)
self.numberLine.objectWillChange.send()
self.numberLine.visible[index].toggle()
print("\(index) now \(self.numberLine.visible[index] ? "shown" : "hidden")")
}) {
Text("Change")
}.padding()
}
}
}
class NumberLine: ObservableObject …Run Code Online (Sandbox Code Playgroud) 我在视图中创建了一个 ObservableObject。
@ObservedObject var selectionModel = FilterSelectionModel()
Run Code Online (Sandbox Code Playgroud)
我在FilterSelectionModel'sinit函数中放置了一个断点,并多次调用它。因为这个 View 是 a 的一部分NavigationLink,所以我知道它是在那时创建的,并且与它一起创建, selectionModel 。当我导航到视图时,将再次创建 selectionModel。
在同一个视图中,我有一个“子视图”,我将 selectionModel 作为一个传递给子视图,EnvironmentObject以便子视图可以更改它。
AddFilterScreen().environmentObject(self.selectionModel)
Run Code Online (Sandbox Code Playgroud)
当子视图被关闭时,selectionModel 将再次创建并且对它所做的更改也消失了。
有趣的提示:在最顶层是一个NavigationView. 如果我添加
.navigationViewStyle(StackNavigationViewStyle())
Run Code Online (Sandbox Code Playgroud)
对此NavigationView,我的 selectionModel 的变化消失了。但是,如果我不添加navigationStyle,则子视图中的 selectionModel 更改仍然存在!!(但我不想要拆分导航视图,我想要堆叠的导航视图)
在这两种情况下 - 有或没有navigationStyle, selectionModel 都会被创建多次。我无法理解这些应该如何可靠地工作。
回到 WWDC 2021\xe2\x80\x99s Discover concurrency in SwiftUI,他们建议您将ObservableObject对象与主要参与者隔离。例如:
struct ContentView: View {\n @StateObject var viewModel = ViewModel()\n\n var body: some View {\n Text("\\(viewModel.count)")\n .task {\n try? await viewModel.start()\n }\n }\n}\n\n@MainActor \nclass ViewModel: ObservableObject {\n var count = 0\n\n func start() async throws {\n while count < 10 {\n count += 1\n try await Task.sleep(for: .seconds(1))\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n但在 iOS 17\xe2\x80\x99s观察框架(如 WWDC 2023\xe2\x80\x99s Discover Observation in SwiftUI中介绍)中,似乎不再需要隔离到主要参与者来防止在后台线程。例如,以下内容不会出现有关从后台启动 UI 更新的警告:
\nstruct ContentView: View {\n …Run Code Online (Sandbox Code Playgroud) 我刚刚更新到 XCode 11.4,我的一些代码已经停止工作。我@Published在一个ObservableObject. 以前,当我更新结构上的属性时,该didSet方法会触发已发布的属性,但现在情况不再如此。在 Swift 的最新更新中,这种行为是否有可能被设计改变了?
这是一个简单的例子:
import SwiftUI
struct PaddingRect {
var left: CGFloat = 20
var right: CGFloat = 20
}
final class SomeStore : ObservableObject {
@Published var someOtherValue: String = "Waiting for didSet"
@Published var paddingRect:PaddingRect = PaddingRect() {
didSet {
someOtherValue = "didSet fired"
}
}
}
struct ObserverIssue: View {
@ObservedObject var store = SomeStore()
var body: some View {
VStack {
Spacer()
Rectangle()
.fill(Color.yellow)
.padding(.leading, store.paddingRect.left)
.padding(.trailing, …Run Code Online (Sandbox Code Playgroud) 在我的应用程序中,我有一系列可以从另一个视图添加的朋友。我已将这组朋友存储在一个类中,该类是ObservableObject. 在我的主视图中FriendListView,我使用ForEach为用户添加的每个朋友显示自定义视图。
我无法添加到数组并让列表显示新项目。根据我能够理解和组合的内容,我尝试了两种不同的尝试。如果有人能够提供任何帮助,我们将不胜感激。我目前有显示第二个视图的方法,我将在其中添加注释掉的新朋友,并且有一种自动保存新朋友的方法作为导航栏项目存在。
如果在类初始值设定项期间将朋友列表附加到数组中,我就成功地显示了朋友列表。调用类方法从另一个视图添加到数组似乎是可行的,因为我能够打印数组的内容,但 UI 不会更新。
这是通过使用一种ForEach(0..<Array.count)方法完成的,但在添加新项目后我收到以下控制台错误。
错误
ForEach<Range<Int>, Int, HStack<TupleView<(Spacer, FriendView, Spacer)>>> count (4) != its initial count (3).
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!
Run Code Online (Sandbox Code Playgroud)
型号代码
ForEach<Range<Int>, Int, HStack<TupleView<(Spacer, FriendView, Spacer)>>> count (4) != its initial count (3).
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use …Run Code Online (Sandbox Code Playgroud) 我分配了一组颜色,希望将其保存到 UserDefaults 中。我有默认的灰色、绿色、橙色和红色,但我想允许颜色选择器更改绿色、橙色和红色。默认值在我的模拟器中有效并显示,但是当我尝试更改数组中的颜色时,出现错误“尝试插入非属性列表对象(\n“50% 灰色”、\n 绿色、\n ” kCGColorSpaceModelRGB 0.647194 0.881984 0.980039 1 “,\n 红色\n) 用于关键 SavedColors。” 我相信这是因为颜色选择器试图插入不同类型的颜色?看起来它可能正在尝试插入 CGColor 或 CGColorSpace ?
这是我的项目代码:
import SwiftUI
import Foundation
import Combine
class UserSettings: ObservableObject {
@Published var colors: [Color] {
didSet {
UserDefaults.standard.set(colors, forKey: "SavedColors")
}
}
init() {
self.colors = UserDefaults.standard.object(forKey: "SavedColors") as? [Color] ?? [Color.gray.opacity(0.5), Color.green, Color.orange, Color.red]
}
}
struct CustomizeView: View {
@ObservedObject var savedColors = UserSettings()
var body: some View {
NavigationView {
Form {
if #available(iOS 14.0, *) …Run Code Online (Sandbox Code Playgroud) 我被认为是 SwiftUI 的新手,我有下面的 ViewModel。但我不确定 MyViewModel 应该是单例的。这样的用法正确吗?符合 ObservableObject 的最佳实践/用法是什么?
class MyViewModel: ObservableObject {
static let shared: MyViewModel = MyViewModel()
@Published var result: String = ""
private init() { }
// some functions
}
struct ContentView: View {
@ObservedObject private var vm = MyViewModel.shared
var body: some View {
Text(vm.result)
}
}
Run Code Online (Sandbox Code Playgroud) 我知道,这是“无法在 iOS XX 中工作”问题之一,但我完全陷入困境......
所以我有一个ObservableObject继承自 的类NSObject,因为我需要监听 的委托方法UISearchResultsUpdating。
class SearchBarListener: NSObject, UISearchResultsUpdating, ObservableObject {
@Published var searchText: String = ""
let searchController: UISearchController = UISearchController(searchResultsController: nil)
override init() {
super.init()
self.searchController.searchResultsUpdater = self
}
func updateSearchResults(for searchController: UISearchController) {
/// Publish search bar text changes
if let searchBarText = searchController.searchBar.text {
print("text: \(searchBarText)")
self.searchText = searchBarText
}
}
}
struct ContentView: View {
@ObservedObject var searchBar = SearchBarListener()
var body: some View {
Text("Search text: …Run Code Online (Sandbox Code Playgroud) 我在 SwiftUI 中观察到的对象有问题。我可以在 View 结构上看到观察对象的变化值。但是在类或函数中,即使我更改了 TextField(可观察对象)的文本值,但“self.codeTwo.text 仍然没有改变。
这是我的代码示例(这是我的 ObservableObject)
class settingCodeTwo: ObservableObject {
private static let userDefaultTextKey = "textKey2"
@Published var text: String = UserDefaults.standard.string(forKey: settingCodeTwo.userDefaultTextKey) ?? ""
private var canc: AnyCancellable!
init() {
canc = $text.debounce(for: 0.2, scheduler: DispatchQueue.main).sink { newText in
UserDefaults.standard.set(newText, forKey: settingCodeTwo.userDefaultTextKey)
}
}
deinit {
canc.cancel()
}
}
Run Code Online (Sandbox Code Playgroud)
主要问题是......“self.codeTwo.text”从未改变!
class NetworkManager: ObservableObject {
@ObservedObject var codeTwo = settingCodeTwo()
@Published var posts = [Post]()
func fetchData() {
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = …Run Code Online (Sandbox Code Playgroud)