草庐IT

ios - 使 TextView 响应用户键入的文本

coder 2023-09-08 原文

我的应用程序中有一个 TextView ,允许用户在其中键入任何内容。我希望 TextView 在输入时检测到一些单词并改变它们的颜色。

例如,如果用户键入“我最喜欢的颜色是红色”,那么我希望单词“红色”获得文本颜色“红色”。但是,thus 应该在句子中输入 red 之后立即发生。我应该如何实现?

最佳答案

你需要三样东西:

  • 在界面生成器中,您需要使用其中一种委托(delegate)方法。我认为“editingChanged”是这里最好的。使用“ctrl”从文本字段拖动到您的文档。

  • 您需要一种方法来遍历文本字段中的字符串并检测单词。

    componentsSeparatedByCharactersInSet

  • 您需要一种方法来为句子中的一个词设置样式。

    NSMutableAttributedString

也很有趣:how to separate a string without removing the delimiter

部分代码(扔在 Playground 上):

这将找到所有要更改的单词,但会删除分隔符。需要做更多工作才能完成代码。

var strSeperator : NSCharacterSet = NSCharacterSet(charactersInString: ",.:;?! ")

var strArray = str.componentsSeparatedByCharactersInSet(strSeperator)

var styledText = NSMutableAttributedString()

for i in 0..<strArray.count {

    let currentWord = strArray[i]
    var currentBoolFoundInControl : Bool = false

    for iB in 0..<colorTheseStrings.count {

        let controlWord = colorTheseStrings[iB]

        if controlWord == currentWord {
            currentBoolFoundInControl = true
            // add to mutable attributed string in a color
        }

    }

    var attrString1 = NSMutableAttributedString(string: "\(strArray[i]) ")

    if currentBoolFoundInControl == true {

        var range = (strArray[i] as NSString).rangeOfString(strArray[i])
        if strArray[i] == "red" {
            attrString1.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0), range: range)
        } else if strArray[i] == "blue" {
            attrString1.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 0.0, green: 0.0, blue: 0.5, alpha: 1.0), range: range)

        }
    }

    styledText.appendAttributedString(attrString1)
}

myTextField.attributedText = styledText

另一种方式:

download it here

我觉得这种方式是最稳健最酷最高效的。 代码可以稍微清理一下,可以使用更多的风格/风格。这对于“天蓝色”之类的东西确实有麻烦,因为它会首先找到“蓝色”并替换它。但我不得不留下一些改进的余地。 ;)

我情不自禁,所以我解决了最后一个问题。

再一次,我修复了,我的修复。现在更有效率并且实际上是正确的。

第一部分是 HighLighter 类,将其放在单独的文档中。

// text hightlighter

class SyntaxGroup {

    var wordCollection : [String] = []
    var type : String = ""
    var color : UIColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)

    init(wordCollection_I : [String], type_I : String, color_I: UIColor) {

        wordCollection = wordCollection_I
        type = type_I
        color = color_I

    }
}

class SyntaxDictionairy {

    var collections : [SyntaxGroup] = []

}

class SyntaxRange {

    var range : NSRange
    var color : UIColor

    init (color_I : UIColor, range_I : NSRange) {
        color = color_I
        range = range_I
    }

}

class HighLighter {

    var ranges : [SyntaxRange] = []
    var baseString : NSMutableString = NSMutableString()
    var highlightedString : NSMutableAttributedString = NSMutableAttributedString()
    var syntaxDictionairy : SyntaxDictionairy

    init (syntaxDictionairy_I : SyntaxDictionairy) {

        syntaxDictionairy = syntaxDictionairy_I

    }

    func run(string : String?, completion: (finished: Bool) -> Void) {

        ranges = [] // reset the ranges, else it crashes when you change a previous part of the text
        highlightedString = NSMutableAttributedString() // make sure all strings start fresh
        baseString = NSMutableString() // make sure all strings start fresh

        // multi threading to prevent locking up the interface with large libraries.
        let qualityOfServiceClass = QOS_CLASS_DEFAULT
        let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
        dispatch_async(backgroundQueue) { () -> Void in

            if string != nil && string != "" {

                self.highlightedString = NSMutableAttributedString(string: string!)

                for i in 0..<self.syntaxDictionairy.collections.count {

                    for iB in 0..<self.syntaxDictionairy.collections[i].wordCollection.count {

                        let currentWordToCheck = self.syntaxDictionairy.collections[i].wordCollection[iB]
                        self.baseString = NSMutableString(string: string!)

                        while self.baseString.containsString(self.syntaxDictionairy.collections[i].wordCollection[iB]) {

                            let nsRange = (self.baseString as NSString).rangeOfString(currentWordToCheck)
                            let newSyntaxRange = SyntaxRange(color_I: self.syntaxDictionairy.collections[i].color, range_I: nsRange)
                            self.ranges.append(newSyntaxRange)

                            var replaceString = ""
                            for _ in 0..<nsRange.length {
                                replaceString += "§" // secret unallowed character
                            }
                            self.baseString.replaceCharactersInRange(nsRange, withString: replaceString)
                        }
                    }
                }
                for i in 0..<self.ranges.count {

                    self.highlightedString.addAttribute(NSForegroundColorAttributeName, value: self.ranges[i].color, range: self.ranges[i].range)

                }
            }

            dispatch_sync(dispatch_get_main_queue()) { () -> Void in

                completion(finished: true)
            }

        }
    }
}

在 View Controller 中: 这是您将与 UITextfield 一起使用的代码。 首先创建一个荧光笔实例并设置你的单词字典和相应的颜色。然后在需要的时候启动run函数。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var myTextfield: UITextField!

    var syntaxHighLighter : HighLighter! // declare highlighter

    override func viewDidLoad() {
        super.viewDidLoad()
        setUpHighLighter()
    }
    // this is just a little function to put the set up for the highlighter outside of viewDidLoad()
    func setUpHighLighter() {

        // build a dict of words to highlight
        let redColor = UIColor(red: 0.5, green: 0.0, blue: 0.0, alpha: 1.0)
        let blueColor = UIColor(red: 0.0, green: 0.0, blue: 0.5, alpha: 1.0)
        let greenColor = UIColor(red: 0.0, green: 0.5, blue: 0.0, alpha: 1.0)

        let redGroup = SyntaxGroup(wordCollection_I: ["red","bordeaux"], type_I: "Color", color_I: redColor)
        let blueGroup = SyntaxGroup(wordCollection_I: ["coralblue","blue","skyblue","azur"], type_I: "Color", color_I: blueColor)
        let greenGroup = SyntaxGroup(wordCollection_I: ["green"], type_I: "Color", color_I: greenColor)

        let dictionairy : SyntaxDictionairy = SyntaxDictionairy()
        dictionairy.collections.append(blueGroup)
        dictionairy.collections.append(greenGroup)
        dictionairy.collections.append(redGroup)

        syntaxHighLighter = HighLighter(syntaxDictionairy_I: dictionairy)

    }
    // this is where the magic happens, place the code from inside the editingChanged inside your function that responds to text changes.
    @IBAction func editingChanged(sender: UITextField) {

        syntaxHighLighter.run(myTextfield.text) { (finished) -> Void in
            self.myTextfield.attributedText = self.syntaxHighLighter.highlightedString
        }
    }
}

关于ios - 使 TextView 响应用户键入的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32356321/

有关ios - 使 TextView 响应用户键入的文本的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby - 按值降序排列散列,然后按升序键入 ruby - 2

    我有这样的哈希trial_hash={"key1"=>1000,"key2"=>34,"key3"=>500,"key4"=>500,"key5"=>500,"key6"=>500}我按值降序排列:my_hash=trial_hash.sort_by{|k,v|v}.reverse我现在是这样理解的:[["key1",1000],["key4",500],["key5",500],["key6",500],["key3",500],["key2",34]]但我希望当值相同时按键的升序排序。我该怎么做?例如:上面的散列将以这种方式排序:[["key1",1000],["key3",500

  4. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  5. ruby-on-rails - 每次我尝试部署时,我都会得到 - (gcloud.preview.app.deploy) 错误响应 : [4] DEADLINE_EXCEEDED - 2

    我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie

  6. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  7. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  8. ruby - RVM "ERROR: Unable to checkout branch ."单用户 - 2

    我在新的Debian6VirtualBoxVM上安装RVM时遇到问题。我已经安装了所有需要的包并使用下载了安装脚本(curl-shttps://rvm.beginrescueend.com/install/rvm)>rvm,但以单个用户身份运行时bashrvm我收到以下错误消息:ERROR:Unabletocheckoutbranch.安装在这里停止,并且(据我所知)没有安装RVM的任何文件。如果我以root身份运行脚本(对于多用户安装),我会收到另一条消息:Successfullycheckedoutbranch''安装程序继续并指示成功,但未添加.rvm目录,甚至在修改我的.bas

  9. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  10. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

随机推荐