【问题标题】:Using stringByReplacingCharactersInRange in Swift在 Swift 中使用 stringByReplacingCharactersInRange
【发布时间】:2014-06-03 12:58:47
【问题描述】:

我正在尝试在 Swift/Xcode6 中使用 UITextFieldDelegate,但我正在为我应该使用 stringByReplacingCharactersInRange 的方式而苦苦挣扎。编译器错误是'无法将表达式的类型'String'转换为类型'$T8'。

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool
{
    let s = textField.text.stringByReplacingCharactersInRange(range:range, withString:string)
    if countElements(s) > 0 {

    } else {

    }
    return true
}

Xcode 6 Beta 5 的更新:问题是 shouldChangeCharactersInRange 提供了一个 NSRange 对象,我们需要一个 Swift Range 对象用于 stringByReplacingCharactersInRange。这仍然可以被认为是一个错误,因为我不明白为什么我们仍然应该处理 NS* 对象?委托方法的 String 参数无论如何都是 Swift 类型。

【问题讨论】:

    标签: swift


    【解决方案1】:

    以下是在各种 Swift 版本中计算结果字符串的方法。

    请注意,所有方法都以完全相同的方式使用-[NSString stringByReplacingOccurrencesOfString:withString:],只是语法不同。

    这是计算结果字符串的首选方法。转换为 Swift Range 并在 Swift String 上使用它很容易出错。例如,在对非 ASCII 字符串进行操作时,Johan 的回答在几个方面是不正确的。

    斯威夫特 3:

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let result = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) ?? string
        // ... do something with `result`
    }
    

    斯威夫特 2.1:

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        let result = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
        // ... do something with `result`
    }
    

    Swift 1(仅留作参考):

    let result = textField.text.bridgeToObjectiveC().stringByReplacingCharactersInRange(range, withString:string)
    

    【讨论】:

    • String 有这个方法。请参阅我的评论。
    • @TomášLinhart String 在哪里声明了这个方法?我在 Swift 模块中找不到它。
    • @NikolaiRuhe 我不知道。但是看看我的帖子并在操场上运行它,你会看到它有效。
    • 至少从 Beta 5 开始,这不再是正确答案。
    • 改用“textField.text as NSString”转换。
    【解决方案2】:

    我为NSRange创建了一个扩展,转换为Range<String.Index>

    extension NSRange {
        func toRange(string: String) -> Range<String.Index> {
            let startIndex = advance(string.startIndex, location)
            let endIndex = advance(startIndex, length)
            return startIndex..<endIndex
        }
    }
    

    所以我可以像这样创建字符串

    let text = textField.text
    let newText = text.stringByReplacingCharactersInRange(range.toRange(text), withString: string)
    

    在 Swift 2.1 中,扩展看起来像:

    extension NSRange {
        func toRange(string: String) -> Range<String.Index> {
            let startIndex = string.startIndex.advancedBy(location)
            let endIndex = startIndex.advancedBy(length)
            return startIndex..<endIndex
        }
    }
    

    【讨论】:

    • 绝对是最 Swift 的答案。但是你可以避免selfs :)
    • @DavideDeFranceschi 为了清楚起见,我添加了selfs。但你是对的,它们是不需要的
    • 这是一个不错的方法,但是当您使用 Unicode 字符(例如表情符号)时它会中断。试一试
    • 在 Swift 2 中,你会使用 advancedBy: let startIndex = string.startIndex.advancedBy(location); let endIndex = startIndex.advancedBy(length)
    • 结束索引不应该提前location + length 吗?
    【解决方案3】:

    我发现的最简单的解决方案是使用as NSString - 这使我们能够使用NSRange

    var textField : UITextField = UITextField()
    textField.text = "this is a test"
    
    let nsRange : NSRange = NSRange(location: 0, length: 4)
    
    let replaced = (textField.text as NSString)
                   .stringByReplacingCharactersInRange(nsRange, withString: "that");
    
    NSLog("Replaced: %@", replaced); //prints "that is a test"
    

    【讨论】:

      【解决方案4】:
      let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
      

      bridgeToObjectiveC 可以在即将到来的更新中移除

      【讨论】:

      • 又短又甜。从 swift2 开始,textField.text 需要强制解包: let newString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
      【解决方案5】:

      除了以下之外,没有什么对我有用:(仅供参考,我使用的是 Xcode7.0 GMSwift 2.0iOS9GM

      func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
          let currentText = textField.text ?? ""
          let prospectiveText = (currentText as NSString).stringByReplacingCharactersInRange(range, withString: string)
          print("prospectiveText", prospectiveText)
          return true;
      }
      

      【讨论】:

        【解决方案6】:

        这是来自this question 的交叉帖子,但没有办法制作Range&lt;String.Index&gt; Swift 原生String.stringByReplacingCharactersInRange() 是非常没用的。所以,这是一个生成Range&lt;String.Index&gt;的函数:

        func RangeMake(#start:Int, #end:Int) -> Range<String.Index> {
            assert(start <= end, "start must be less than or equal to end")
            func rep(str: String, count: Int) -> String {
                var result = ""
                for i in 0..count {
                    result += str
                }
                return result
            }
            let length = end - start
            let padding = rep(" ", start)
            let dashes = rep("-", length)
            let search = padding + dashes
            return search.rangeOfString(dashes, options: nil, range: Range(start: search.startIndex, end: search.endIndex), locale: NSLocale.systemLocale())
        }
        
        let sourceString = "Call me Ishmael."
        let range = RangeMake(start: 8, end: 15)    
        let name = sourceString.substringWithRange(range)
        // name = "Ishmael"
        

        【讨论】:

          【解决方案7】:

          工作和测试

          func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
              let newString = NSString(string: textField.text!).replacingCharacters(in: range, with: string)
          
              print(newString)
          
                return true;
          }
          

          【讨论】:

            【解决方案8】:

            从 Swift 4 开始,这有点简单,就像 Alexander Volkov 的回答一样,但没有扩展。

               func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
                    let revisedText: String
                    if let text = textField.text, let swiftRange = Range(range, in: text) {
                        revisedText = text.replacingCharacters(in: swiftRange, with: string)
                    } else {
                        revisedText = string
                    }
                    // Do something with the text and return boolean.
               }
            

            【讨论】:

            • let 更改为 var 以使 revisedText 可变。
            【解决方案9】:

            创建 String.Index 很麻烦。

            let string = "hello"
            let range = string.startIndex .. string.startIndex.succ().succ()
            let result = string.stringByReplacingCharactersInRange(range, withString: "si")
            

            【讨论】:

            • 我会将此作为错误发布。应该有更好的方法来创建字符串范围。或者我只是找不到它。
            • 欢迎举报。我也没有找到更好的方法。
            • 我同意,我的直觉是这是一个错误。例如 stringByAppendingString 按预期工作。
            【解决方案10】:

            对于 iOS 8.3,使用以下代码

             func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
            {
                if textField.isEqual(<textField whose value to be copied>)
                {
                    <TextField to be updated>.text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
                }
            
                return true
            }
            

            【讨论】:

              【解决方案11】:

              在 Swift 2.0 中,必须更改来自 Durul 的答案,因为必须使用 characters.count 而不是 count()。

              必须执行以下操作。

              func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
                  let length = textField.text!.characters.count - range.length + string.characters.count
                  if length > 0 {
                      submitButton.enabled = true
                  } else {
                      submitButton.enabled = false
                  }
                  return true
              }
              

              【讨论】:

                【解决方案12】:
                import UIKit
                
                class LoginViewController: UIViewController, UITextFieldDelegate {
                
                    @IBOutlet weak var submitButton: UIButton!
                
                    override func viewDidLoad() {
                        super.viewDidLoad()
                
                        // Do any additional setup after loading the view.
                    }
                
                    override func didReceiveMemoryWarning() {
                        super.didReceiveMemoryWarning()
                        // Dispose of any resources that can be recreated.
                    }
                
                
                
                    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
                        let length = count(textField.text) - range.length + count(string)
                        if length > 0 {
                            submitButton.enabled = true
                        } else {
                            submitButton.enabled = false
                        }
                        return true
                    }
                }
                

                【讨论】:

                  【解决方案13】:

                  斯威夫特 4:

                  func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
                      var text = textField.text ?? ""
                      text.replaceSubrange(range.toRange(string: text), with: string)
                      ...
                      return true
                  }
                  
                  extension NSRange {
                  
                      /// Convert to Range for given string
                      ///
                      /// - Parameter string: the string
                      /// - Returns: range
                      func toRange(string: String) -> Range<String.Index> {
                          let range = string.index(string.startIndex, offsetBy: self.lowerBound)..<string.index(string.startIndex, offsetBy: self.upperBound)
                          return range
                      }
                  }
                  

                  【讨论】:

                  • 当我删除一个字符时,我得到一个Fatal error: Can't advance past endIndex
                  • @Maximelc 建议的方法是正确的。提供您的代码。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-08-06
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-05-26
                  • 1970-01-01
                  • 2017-03-01
                  相关资源
                  最近更新 更多