SwiftUI: is there exist modifier to highlight substring of Text() view?

后端 未结 4 446
终归单人心
终归单人心 2020-12-18 01:52

I have some text on the screen:

Text(\"someText1\")

is it possible to highlight/select part of the text without creating lot Text items

4条回答
  •  眼角桃花
    2020-12-18 01:59

    Disclaimer: I was really reluctant to post my answer since I am sure that there must be a lot of way-smarter, way-better (I dunno maybe a wrapper to a UIKit view using TextKit) and more robust approaches, but... what the heck I thought it was a fun exercise and maybe someone could actually benefit from it.

    So here we go:

    Instead of a modifier, I'm going to make a view that holds a string (to render) and another one to hold our 'matching' text.

    struct HighlightedText: View {
        let text: String
        let matching: String
    
        init(_ text: String, matching: String) {
            self.text = text
            self.matching = matching
        }
    
        var body: some View {
            let tagged = text.replacingOccurrences(of: self.matching, with: ">\(self.matching)")
            let split = tagged.components(separatedBy: "")
            return split.reduce(Text("")) { (a, b) -> Text in
                guard !b.hasPrefix(">") else {
                    return a + Text(b.dropFirst()).foregroundColor(.red)
                }
                return a + Text(b)
            }
        }
    }
    

    I guess the code is quite self-explanatory but in a nutshell:

    1. Find all matches
    2. Replace them by hardcoded 'tags' (marking the beginning of a match by another hardcoded character)
    3. Split on the tags
    4. Reduce the components & return a stylized version if we're on a match

    Now, we can use it with something like this:

    struct ContentView: View {
        @State var matching: String = "ll"
        var body: some View {
            VStack {
                TextField("Matching term", text: self.$matching)
                HighlightedText("Hello to all in this hall", matching: self.matching)
                .font(.largeTitle)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
    

    Here is a (crappy) gif demonstrating it in action:

    https://imgur.com/sDpr0Ul

    Finally, in case you're wondering how I'm running SwiftUI outside Xcode, here is a gist I've made for prototyping quickly in SwiftUI on the Mac

提交回复
热议问题