使用绑定确定SwiftUI中的字符串更改

使用绑定确定SwiftUI中的字符串更改,swift,string,swiftui,Swift,String,Swiftui,这是我关于stackoverflow的第一个问题,一个月前我才开始使用iOS、Swift和SwiftUI。请谅解,如果我做错了什么,请告诉我 我想确定是否在绑定了字符串(@Binding)的结构中更改了字符串。我在开始时将原始字符串复制到第二个字符串(在主体内)。两个字符串总是相同的,就像复制了引用而不是值一样。就我所了解的字符串文档而言,字符串总是使用其值进行复制。如果我错了,请纠正我。下面是一些示例代码: struct EditTextSheet: View { var title: St

这是我关于stackoverflow的第一个问题,一个月前我才开始使用iOS、Swift和SwiftUI。请谅解,如果我做错了什么,请告诉我


我想确定是否在绑定了字符串(@Binding)的结构中更改了字符串。我在开始时将原始字符串复制到第二个字符串(在主体内)。两个字符串总是相同的,就像复制了引用而不是值一样。就我所了解的字符串文档而言,字符串总是使用其值进行复制。如果我错了,请纠正我。下面是一些示例代码:

struct EditTextSheet: View {

var title: String
@Binding var showSheetView: Bool
@Binding var userConfirmedChange: Bool
@Binding var textToEdit: String

var body: some View
{
    // use to determine if user made any changes to text
    let originalText: String = textToEdit
    
    
    NavigationView
    {
        Form
        {
            Section(header: Text(title))
            {
                TextField(textToEdit, text: $textToEdit)
            }
            
        }
        
        .navigationBarTitle("", displayMode: .inline)
        // buttons within navigation Bar OK top right, cancel top left. OK also sets userConfirmedChange to true
        
        // Cancel BUTTON
        .navigationBarItems(leading: Button(action: {
            self.showSheetView = false
            
        }) {
            Text("Cancel")
        }, trailing: Button(action: {
            
            // OK BUTTON
            if textToEdit.isEmpty
            {
                // tell user that String cannot be empty
                ...
            }
            else
            {



                // ***BELOW IS ALWAYS THE SAME***
                // if "Test" was passed as textToEdit and user then
                // changes textToEdit via the TextField to "Test1234" 
                // below if clause results in "Test1234" == "Test1234"
                // what I want is "Test" == "Test1234"




                if textToEdit == originalText
                {
                    self.userConfirmedChange = false
                    self.showSheetView = false
                }
                else
                {
                    self.userConfirmedChange = true
                    self.showSheetView = false
                }
                
            }
            
        }) {
            Text("OK")
                .bold()
        })
        
    }
    
    
}

}
此结构是一个工作表,其调用方式如下:

    ...
    @State var showEditSheet = false
    @State var userConfirmedChange = false
    @State var editText: String = ""
    ...
   
    Button(action: {
            editText = "Test"
            userConfirmedChange = false
            showEditSheet.toggle()
        }) {
            Text ("Edit Test")
        }.sheet(isPresented: $showEditSheet, onDismiss: {
            if userConfirmedChange
            {
                print("\(editText)")
            }
            else
            {
                print("cancelled")
            }
        }) {
            EditTextSheet(title: "some title", showSheetView: $showEditSheet, userConfirmedChange: $userConfirmedChange, textToEdit: $editText)
        }

         ...

除了在视图主体内初始化“let originalText:String=textToEdit”之外,您的代码还可以。当“@Binding var textToEdit:String”变量更改时,整个视图将重新呈现,这也会再次初始化originalText。解决方法是将原始文本移到身体外部

简单的方法是将originalText作为另一个变量添加到初始值设定项:

   @Binding var textToEdit: String
   let originalText: String

    var body: some View {

您正面临状态更新问题,状态更新会覆盖旧值。您正在将
textToEdit
作为绑定传递到
EditTextSheet
视图。每当此值更改时,将重新加载具有该状态的父视图。因此,它将重新加载EditTextSheet并将
originalText
设置为当前值。因此,
originalText
textToEdit
将始终相同。这就是为什么你比较它们,它们总是一样的<代码>字符串是值类型,而不是引用类型。但在你的情况下,它们总是有相同的价值

要解决此问题,您需要在
EditTextSheet
中使用一个局部状态变量,该变量接受绑定的值。此状态在您的视图中是本地状态,将存储新值。运行操作时,您将把未更改的绑定与该状态(表示TextField)进行比较,然后运行操作。下面是对代码的可能更改

struct EditTextSheet: View {

    var title: String
    @Binding var showSheetView: Bool
    @Binding var userConfirmedChange: Bool
    @Binding var textToEdit: String
    @State var actualText : String//<< this is the local State which will be used for the TextField

    init(title: String, showSheetView: Binding<Bool>, userConfirmedChange: Binding<Bool>, textToEdit: Binding<String>) {
        self.title = title
        self._showSheetView = showSheetView
        self._userConfirmedChange = userConfirmedChange
        self._textToEdit = textToEdit
        self._actualText = State(initialValue: textToEdit.wrappedValue) //<< create new State with the value of the Binding, now it will be initialized with "Test"
    }
现在,在按钮操作中,我们可以使用以下代码:

if actualText.isEmpty
{
    
}
else
{
    //This is our new value which is binded to the textField
    print(actualText)
    //This is our old value
    print(textToEdit)

    if actualText == textToEdit
    {
        self.textToEdit = actualText
        self.userConfirmedChange = false
        self.showSheetView = false
    }
    else
    {
        self.textToEdit = actualText
        self.userConfirmedChange = true
        self.showSheetView = false
    }

我们检查旧值是否与新值匹配。在这里,我们将本地状态(发生了变化)与旧绑定(到目前为止从未发生过变化)进行比较。最后,我们将使用
self.textToEdit=actualText
用实际值更新绑定,因为我们没有将该绑定分配给TextField。当我们关闭此视图时,TextToEdit将被传递回父视图。

如果结构中的字符串发生更改,好的,当字符串发生更改时,您想调用某个通知您字符串已更改的内容吗?“有一个绑定字符串(@Binding)”是什么意思?如果你将一个变量声明为绑定,那么它当然是绑定的?!“具有绑定字符串(@Binding)”只是为了强调,我从中复制的变量是(AT)绑定的var:String,而不是“普通”的var:String。(AT)绑定不应影响“let originalText=textToEdit”变量,只应影响传递到此结构的变量。至少这是我想要实现的,以确定用户在与TextField交互时是否更改了字符串感谢您的回答和对我的错误所在的澄清。你的答案很好。我倾向于避免调用我的视图两次传递相同字符串的冗余。感谢您的解释和使用自定义初始化方法获取绑定第一个状态的解决方案。最后,我简单地将originalText设置为editText的第一个状态,就像您对actualText所做的那样,然后保持我原来的行为。工作很好,而且(对我来说)比将editText设置为实际文本更容易理解。但是,再次感谢!!
if actualText.isEmpty
{
    
}
else
{
    //This is our new value which is binded to the textField
    print(actualText)
    //This is our old value
    print(textToEdit)

    if actualText == textToEdit
    {
        self.textToEdit = actualText
        self.userConfirmedChange = false
        self.showSheetView = false
    }
    else
    {
        self.textToEdit = actualText
        self.userConfirmedChange = true
        self.showSheetView = false
    }