swiftUI:设置更改后更新文本

Posted

技术标签:

【中文标题】swiftUI:设置更改后更新文本【英文标题】:swiftUI : Updating Text after changes in settings 【发布时间】:2020-05-21 18:27:46 【问题描述】:

[编辑(1) 以反映posting of streamlined app 以说明问题:]。 [编辑(2):完全删除 EnvironmentObject 和应用程序现在可以工作了!不理解为什么会刷新正文,因为没有修改 @State 变量...文本末尾的代码]

我正在编写一个应用程序,它在某些时候会根据一组规则显示一些与 2 个数组的内容相关的文本。这些规则可以在设置视图中设置,作为用户的偏好。 因此,当用户更改他希望在“设置”中应用的规则时,需要重新评估该文本。 但当然,事情并不那么容易。 我在主 ContentView 上将设置视图显示为模态,当我关闭该模态时,不会重绘 ContentView 的主体...

我使用 @Published 变量创建了一个 EnvironmentObject,以跟踪所有用户偏好(也写入 UserDefaults),并与我的 ContentView 和 SettingsView 共享该 @EnvironmentObject,希望成为一个被观察的对象,它的更改会触发我的 ContentView 的刷新。

不是这样……

有什么想法可以帮助我继续前进吗?任何指针将不胜感激(再次!)。

Posted app on GitHub 具有以下架构:

一个appState环境对象,ContentView 显示一组文本,具体取决于在 一个settingsView

UserDefaults 在AppDelegate 中初始化。

感谢您对此的任何帮助...

内容视图:


import SwiftUI
struct ContentView: View 

  @State var modalIsPresented = false // The "settingsView" modally presented as a sheet

  @State private var modalViewCaller = 0 // This triggers the appropriate modal (only one in this example)

    var body: some View 

        NavigationView 
            VStack 
                Spacer()
                    VStack 
                    Text(generateStrings().text1)
                        .foregroundColor(Color(UIColor.systemGreen))
                    Text(generateStrings().text2)
                     // end of VStack
                        .frame(maxWidth: .infinity, alignment: .center)
                        .lineLimit(nil) // allows unlimited lines
                        .padding(.all)
                Spacer()
             // END of main VStack
            .onAppear() 
                self.modalViewCaller = 0
            
            .navigationBarTitle("Test app", displayMode: .inline)
            .navigationBarItems(leading: (
                Button(action: 
                    self.modalViewCaller = 6 // SettingsView
                    self.modalIsPresented = true
                
                    ) 
                        Image(systemName: "gear")
                            .imageScale(.large)
                     
            ))
         // END of NavigationView
        .sheet(isPresented: $modalIsPresented, content: sheetContent)
        .navigationViewStyle(StackNavigationViewStyle()) // This avoids dual column on iPad

     // END of var body: some View

  // MARK: @ViewBuilder func sheetContent() :

    @ViewBuilder func sheetContent() -> some View 

        if modalViewCaller == 6 
            SettingsView()
            
     // END of func sheetContent


    // MARK: generateStrings() : -

    func generateStrings() -> (text1: String, text2: String, recapText: String, isHappy: Bool)  // minimumNumberOfEventsCheck

        var myBool = false
        var aString = "" // The text 1 string
        var bString = "" // The text 2 string
        var cString = "" // The recap string

        if UserDefaults.standard.bool(forKey: kmultiRules)  // The user chose the dual rules option
            let ruleSet = UserDefaults.standard.integer(forKey: kruleSelection) + 1
            aString = "User chose 2 rules option"
            bString = "User chose rule set # \(ruleSet)"
            myBool = true
            print("isDualRules true loop : generateStrings was called at \(Date().debugDescription)")
            cString = "Dual rules option, user chose rule set nb \(ruleSet)"
        
            else // The user chose the single rule option
        
            aString = "User chose single rule option"
            bString = "User had no choice : there is only one set of rules !"
            myBool = false
            print("isDualRules false loop : generateStrings was called at \(Date().debugDescription)")
            cString = "Single rule option, user chose nothing."
        


            return (aString, bString, cString, myBool)
     // End of func generatestrings() -> String



struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        return ContentView()

    


设置视图:

import SwiftUI
import UIKit

struct SettingsView: View 

    @Environment(\.presentationMode) var presentationMode // in order to dismiss the Sheet

    @State public var multiRules = UserDefaults.standard.bool(forKey: kmultiRules)

    @State private var ruleSelection = UserDefaults.standard.integer(forKey: kruleSelection) // 0 is rule 1, 1 is rule 2


    var body: some View 

        NavigationView 
            List 

                Toggle(isOn: $multiRules)
                
                    Text("more than one rule ?")
                
                .padding(.horizontal)

                if multiRules 
                            Picker("", selection: $ruleSelection)
                                Text("rules 1").tag(0)
                                Text("rules 2").tag(1)
                            .pickerStyle(SegmentedPickerStyle())
                            .padding(.horizontal)
                        

             // End of List
            .navigationBarItems(
                leading:
                Button("Done") 
                        self.saveDefaults() // We try to save once more if needed
                        self.presentationMode.wrappedValue.dismiss() // This dismisses the view
                
            )
                .navigationBarTitle("Settings", displayMode: .inline)
         // END of Navigation view
     // END of some View


    func saveDefaults() 
        UserDefaults.standard.set(multiRules, forKey: kmultiRules)
        UserDefaults.standard.set(ruleSelection, forKey: kruleSelection)
        




// MARK: Preview struct

struct SettingsView_Previews: PreviewProvider 
    static var previews: some View 

        return SettingsView()
    


Constants.swift 文件:

import Foundation
import SwiftUI

let kmultiRules = "two rules"
let kruleSelection = "rules selection"
let kappStateChanged = "appStateChanged"

AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        // Override point for customization after application launch.

        UserDefaults.standard.register(defaults: [ // We initialize the UserDefaults
        "two rules": false,
        "rules selection": 0, // 0 is ruel 1, 1 is rule 2
        "appStateChanged": false
        ])

        return true
    

【问题讨论】:

让我们从问题的演示代码开始...... 阅读并考虑此***.com/help/minimal-reproducible-example 和此***.com/help/how-to-ask。这不是一个“我们免费为您编写代码”的平台,而是一个“我们帮助您编写到目前为止所获得的代码”的平台。 嗯,我从不认为这个平台是一个“免费代码”场所。我总是提供清晰的代码来说明我的任何问题。我只是在这里寻找一个指针......我的应用程序的代码现在很长,我很难提取一个工作骨架来提供代码。明天将调查通知中心,这似乎对我的问题很有希望。 按照 Olaf 的建议,通过在 onDissappear 中触发通知中心方法来尝试通知中心方法,不高兴... .. 谢谢。 github.com/Esowes/refreshFromModal 【参考方案1】:

如果您在两个视图中有一个具有@Published 属性的共享@EnvironmentObject,如果您从一个视图更改此类属性,则另一个将重新执行body 属性并更新视图。

创建简单的独立示例确实很有帮助 - 不仅可以在这里提问,还可以更深入地了解/了解为什么它在复杂情况下不起作用。

例如:

import SwiftUI

class TextSettings: ObservableObject 
    @Published var count: Int = 1


struct TextSettingsView: View 
    @EnvironmentObject var settings: TextSettings

    var body: some View 
        Form 
            Picker(selection: $settings.count, label:
                Text("Text Repeat Count"))
            
                ForEach(Array(1...5), id: \.self)  value in
                    Text(String(value)).tag(value)
                
            
        
    


struct TextWithSettingExampleView: View 
    @EnvironmentObject var settings: TextSettings

    var body: some View 
        Text(String(repeating: "Hello ", count: Int(settings.count)))
            .navigationBarItems(trailing: NavigationLink("Settings", destination: TextSettingsView()))
    



struct TextWithSettingExampleView_Previews: PreviewProvider 
    static var previews: some View 
        NavigationView 
            TextWithSettingExampleView()
        
        .environmentObject(TextSettings())
    

【讨论】:

感谢 Ralf 的洞察力,我更新了我的问题:我构建了一个示例项目 (github.com/Esowes/refreshFromModal),但仍然无法使其正常工作。我正在更改环境对象的 @Published 属性,没有成功...【参考方案2】:

不确定我是否完全理解这个问题,但我有一个我认为可能是类似问题的问题,当从模式触发更改时,我从来没有让我的内容视图反映我观察到的对象中的更新。我通过在我观察到的对象中触发一个动作来解决/破解这个问题,当像这样解除模态时:

struct ContentView: View 
//
@State var isPresentingModal = false
var body: some View 
//
.sheet(isPresented: self.$isPresentingModal) 
                        PresentedModalView()
                            .onDisappear 
                                //Do something here
                         
                       
                     
                   

【讨论】:

好主意,因为这个 onDisappear 总是被触发,与 onAppear 相反。但是文本仍然不会更新!我通过尽可能简化现有应用程序创建的示例应用程序链接编辑了我的问题:github.com/Esowes/refreshFromModal 浏览了您的项目,您是否尝试过在内容视图中从 onDisappear 调用“self.appState.updateValues()”?只是一个疯狂的猜测。 是的,我做到了,不高兴......我完全解构了我的应用程序并摆脱了环境对象,将我的 generateStrings 函数中的 if 参数更改为直接的 UserDefaults 值,现在,我的 ContentView 已刷新,即使没有修改 @State 变量!简而言之,该应用程序有效,但我不知道为什么...将使用我的代码编辑问题...

以上是关于swiftUI:设置更改后更新文本的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 位置更改时 SwiftUI 更新文本

如何在swiftui中使用选择器更改文本大小

解析json数据后更新swiftui文本视图

输入 SwiftUI 后更改文本字段的焦点

如何在 SwiftUI 中动画/过渡文本值更改

SwiftUI 表单中的文本在更改后不换行