Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
ios-输入时替换/删除UITextView中的字符[•;]_Ios_Swift_Uitextview_Nsrange_Nstextstorage - Fatal编程技术网

ios-输入时替换/删除UITextView中的字符[•;]

ios-输入时替换/删除UITextView中的字符[•;],ios,swift,uitextview,nsrange,nstextstorage,Ios,Swift,Uitextview,Nsrange,Nstextstorage,我有一个带有自定义NSTextStorage的UITextView,如果前一行以列表项目符号开头,我会在每次输入后添加一个列表项目符号 当用户输入并且光标所在的行的开头只有一个列表项目符号时,我将删除列表项目符号并停留在该行 第一个函数按预期工作。但我很难想出如何删除列表中的项目符号 上面的代码不起作用。我可能没有使用正确的范围 这是我所有的代码 class TextView: UITextView { internal var storage: TextStorage!

我有一个带有自定义NSTextStorage的UITextView,如果前一行以列表项目符号开头,我会在每次输入后添加一个列表项目符号

当用户输入并且光标所在的行的开头只有一个列表项目符号时,我将删除列表项目符号并停留在该行

第一个函数按预期工作。但我很难想出如何删除列表中的项目符号


上面的代码不起作用。我可能没有使用正确的范围

这是我所有的代码

class TextView: UITextView {

     internal var storage: TextStorage!

     var defaultAttributes: [NSAttributedString.Key: AnyObject] = [:]

     override init(frame: CGRect, textContainer: NSTextContainer?) {

         let container = (textContainer == nil) ? NSTextContainer() : textContainer!
         container.widthTracksTextView = true
         container.heightTracksTextView = true

         let layoutManager = NSLayoutManager()
         layoutManager.addTextContainer(container)
         self.storage = TextStorage()
         self.storage.addLayoutManager(layoutManager)

         super.init(frame: .zero, textContainer: container)

         self.textContainerInset  = .init(top: 16, left: 16, bottom: 16, right: 16)
         self.isScrollEnabled = true
         self.storage.textView = self
     }

     required init?(coder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
 }
文本存储
总之,我只需要在用户输入并停留在该行时删除光标所在的单词

如果你能帮我解决这个问题,我将非常感激

更新 如何测试此代码

  • 在第一行,添加一个项目符号+一个空格[•],写几个单词并输入
  • 从这一点开始,它添加了一个项目符号,因为前一行以项目符号开头

  • 找到下面修改过的函数。使用Xcode 11.4/iOS 13.4进行测试

    override func replaceCharacters(范围:NSRange,带str:String){
    变量listPrefix:字符串?=nil
    if(TextUtils.isReturn(str:str)){
    让currentLine=TextUtils.startOffset(self.string,位置:range.location)。0
    设separateds=currentLine.components(separatedBy:“”)
    如果分隔.first!。包含(“•”&¤tLine.trimmingCharacters(在:。空格中)。计数==1{
    listPrefix=“”
    }
    否则{
    如果separateds.count>=2{
    如果分隔。第一个!包含(“•”){
    listPrefix=“•”
    }
    }
    }
    }
    开始编辑
    backingStore.replaceCharacters(在:范围内,带:str)
    已编辑(.editedCharacters,范围:range,changeInLength:(str作为NSString.length-range.length)
    endEditing()
    guard let prefix=listPrefix-else{
    返回
    }
    如果前缀为.isEmpty{
    让text=string.split(分隔符:“\n”)
    let next=NSMakeRange(textView.selectedRange.location-(text.last!.count)-1,text.last!.count+1)
    ReplaceCharactersRange(下一步,带字符串:“”,selectedRangeLocationMove:0)
    }否则{
    让newRange=NSMakeRange(textView.selectedRange.location+str.count,0)
    ReplaceCharactersRange(新范围,带字符串:前缀,selectedRangeLocationMove:prefix.count)
    }
    }
    重写func setAttributes(attrs:[NSAttributedString.Key:Any]?,范围:NSRange){
    
    guard range.upperBound此代码不按声明的方式工作。使用Xcode 11.4/iOS 13.4测试,TextView是以编程方式创建的。列表项目符号从未插入任何输入的文本,因此用例和期望值不清楚。是否更新代码?
    class TextView: UITextView {
    
         internal var storage: TextStorage!
    
         var defaultAttributes: [NSAttributedString.Key: AnyObject] = [:]
    
         override init(frame: CGRect, textContainer: NSTextContainer?) {
    
             let container = (textContainer == nil) ? NSTextContainer() : textContainer!
             container.widthTracksTextView = true
             container.heightTracksTextView = true
    
             let layoutManager = NSLayoutManager()
             layoutManager.addTextContainer(container)
             self.storage = TextStorage()
             self.storage.addLayoutManager(layoutManager)
    
             super.init(frame: .zero, textContainer: container)
    
             self.textContainerInset  = .init(top: 16, left: 16, bottom: 16, right: 16)
             self.isScrollEnabled = true
             self.storage.textView = self
         }
    
         required init?(coder: NSCoder) {
             fatalError("init(coder:) has not been implemented")
         }
     }
    
    class TextStorage: NSTextStorage {
    
            var backingStore: NSMutableAttributedString = NSMutableAttributedString()
            var textView: UITextView!
    
            override var string: String {
                return self.backingStore.string
            }
    
            override init() {
                super.init()
            }
    
            required init?(coder aDecoder: NSCoder) {
                super.init(coder: aDecoder)
            }
    
            override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key: Any] {
                return backingStore.attributes(at: location, effectiveRange: range)
            }
    
            override func replaceCharacters(in range: NSRange, with str: String) {
    
                var listPrefix: String? = nil
                if (TextUtils.isReturn(str: str)) {
    
                    let currentLine = TextUtils.startOffset(self.string, location: range.location).0
                    let separateds = currentLine.components(separatedBy: " ")
    
                    if separateds.first!.contains("•") && currentLine.trimmingCharacters(in: .whitespaces).count == 1  {
                        listPrefix = ""
                    }
    
                    else {
                        if separateds.count >= 2 {
                            if separateds.first!.contains("•") {
                                listPrefix = "• "
                            }
                        }
                    }
                }
    
                beginEditing()
    
                backingStore.replaceCharacters(in: range, with:str)
                edited(.editedCharacters, range: range, changeInLength: (str as NSString).length - range.length)
                endEditing()
    
    
                guard let prefix = listPrefix else {
                    return
                }
    
    
                if  prefix.isEmpty {
                    let text = string.split(separator: "\n")
                    let next = NSMakeRange(textView.selectedRange.location - (text.last!.count) ,0)
                    replaceCharactersInRange(next, withString: " ", selectedRangeLocationMove: text.last!.count)
                } else {
                    let newRange  = NSMakeRange(textView.selectedRange.location + str.count, 0)
                    replaceCharactersInRange(newRange, withString: prefix, selectedRangeLocationMove: prefix.count)
                }
            }
    
    
        override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
            beginEditing()
            backingStore.setAttributes(attrs, range: range)
            edited(.editedAttributes, range: range, changeInLength: 0)
            endEditing()
        }
    
        func replaceCharactersInRange(_ replaceRange: NSRange, withString str: String, selectedRangeLocationMove: Int) {
            if textView.undoManager!.isUndoing {
                textView.selectedRange = NSMakeRange(textView.selectedRange.location - selectedRangeLocationMove, 0)
                replaceCharactersInRange(NSMakeRange(replaceRange.location, str.count), withString: "")
            } else {
                replaceCharactersInRange(replaceRange, withString: str)
                textView.selectedRange = NSMakeRange(textView.selectedRange.location + selectedRangeLocationMove, 0)
            }
        }
    }
    
    extension NSMutableAttributedString {
    
        func replaceCharactersInRange(_ range: NSRange, withString str: String) {
            if isSafeRange(range) {
                replaceCharacters(in: range, with: str)
            }
        }
    
        func isSafeRange(_ range: NSRange) -> Bool {
            if range.location < 0 {
                return false
            }
            let maxLength = range.location + range.length
            return maxLength <= string.count
        }
    }
    
    
    
    class TextUtils {
    
        class func isReturn(str: String) -> Bool {
            return str == "\n"
        }
    
        class func isBackspace(str: String) -> Bool {
            return str == ""
        }
    
        class func startOffset(_ string: String, location: Int) -> (String, Int) {
    
            var offset: Int = 0
            var word = NSString(string: string).substring(to: location)
            let lines = string.components(separatedBy: "\n")
    
            if lines.count > 0 {
                let last = lines.last!
    
                offset = word.count - last.count
                word = last
            }
    
            return (word, offset)
        }
    }
    
    replaceCharactersInRange(next, withString: " ", selectedRangeLocationMove: text.last!.count)
    
    override func replaceCharacters(in range: NSRange, with str: String) {
    
        var listPrefix: String? = nil
        if (TextUtils.isReturn(str: str)) {
    
            let currentLine = TextUtils.startOffset(self.string, location: range.location).0
            let separateds = currentLine.components(separatedBy: " ")
    
            if separateds.first!.contains("•") && currentLine.trimmingCharacters(in: .whitespaces).count == 1  {
                listPrefix = ""
            }
    
            else {
                if separateds.count >= 2 {
                    if separateds.first!.contains("•") {
                        listPrefix = "• "
                    }
                }
            }
        }
    
        beginEditing()
        backingStore.replaceCharacters(in: range, with:str)
        edited(.editedCharacters, range: range, changeInLength: (str as NSString).length - range.length)
        endEditing()
    
    
        guard let prefix = listPrefix else {
            return
        }
    
    
        if  prefix.isEmpty {
            let text = string.split(separator: "\n")
            let next = NSMakeRange(textView.selectedRange.location - (text.last!.count) - 1, text.last!.count + 1)
            replaceCharactersInRange(next, withString: " ", selectedRangeLocationMove: 0)
        } else {
            let newRange  = NSMakeRange(textView.selectedRange.location + str.count, 0)
            replaceCharactersInRange(newRange, withString: prefix, selectedRangeLocationMove: prefix.count)
        }
    }
    
    
    override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
        guard range.upperBound <= string.count else { return }
    
        beginEditing()
        backingStore.setAttributes(attrs, range: range)
        edited(.editedAttributes, range: range, changeInLength: 0)
        endEditing()
    }