Using character delimiters to find and highlight text in Swift

橙三吉。 提交于 2020-01-01 15:32:12

问题


I previously developed an android app that served as a reference guide to users. It used a sqlite database to store the information. The database stores UTF-8 text without formatting (i.e. bold or underlined)

To highlight what sections of text required formatting I enclosed them using delimiter tokens specifically $$ as this does not appear in the database as information. Before displaying the text to the user I wrote a method to find these delimiters and add formatting to the text contained within them and delete the delimiters. so $$foo$$ became foo.

My java code for this is as follows:

private static CharSequence boldUnderlineText(CharSequence text, String token) {
    int tokenLen = token.length();
    int start = text.toString().indexOf(token) + tokenLen;
    int end = text.toString().indexOf(token, start);

    while (start > -1 && end > -1)
    {
        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);
        //add the formatting required
        spannableStringBuilder.setSpan(new UnderlineSpan(), start, end, 0);
        spannableStringBuilder.setSpan(new StyleSpan(Typeface.BOLD), start, end, 0);

        // Delete the tokens before and after the span
        spannableStringBuilder.delete(end, end + tokenLen);
        spannableStringBuilder.delete(start - tokenLen, start);
        text = spannableStringBuilder;

        start = text.toString().indexOf(token, end - tokenLen - tokenLen) + tokenLen;
        end = text.toString().indexOf(token, start);
    }

    return text;
}

I have recreated my app in Swift for iOS and it is complete apart from showing the correct formatting. It appears that Swift treats strings differently from other languages.

So far I have tried using both NSString and String types for my original unformatted paragraph and get manage to get the range, start and end index of the first delimiter:

func applyFormatting2(noFormatString: NSString, delimiter: String){
    let paragraphLength: Int = noFormatString.length //length of paragraph
    let tokenLength: Int = delimiter.characters.count //length of token

    let rangeOfToken = noFormatString.rangeOfString(formatToken) //range of the first delimiter
    let startOfToken = rangeOfToken.toRange()?.startIndex //start index of first delimiter
    let endOfToken = rangeOfToken.toRange()?.endIndex //end index of first delimiter

    var startOfFormatting = endOfToken //where to start the edit (end index of first delimiter)



}

OR

func applyFormatting(noFormatString: String, token: String){
    let paragraphLength: Int = noFormatString.characters.count
    let tokenLength: Int = token.characters.count   //length of the $$ Token (2)
    let rangeOfToken = noFormatString.rangeOfString(formatToken)    //The range of the first instance of $$ in the no format string
    let startOfToken = rangeOfToken?.startIndex //the starting index of the found range for the found instance of $$
    let endOfToken = rangeOfToken?.endIndex //the starting index of the found range for the found instance of $$


    var startOfFormatting = endOfToken
}

I appreciate this code is verbose and has pointless variables but it helps me think though my code when I'm working out a problem.

I am currently struggling to workout how to find the second/closing delimiter. I want to search through the string from a specific index as I did in Java using the line

int end = text.toString().indexOf(token, start);

however I cannot work out how to do this using ranges.

Can anyone help me out with either how to correctly identify where the closing delimiter is or how to complete the code block to format all the required text?

Thanks

Aldo


回答1:


How about using NSRegularExpression?

public extension NSMutableAttributedString {
    func addAttributes(attrs: [String : AnyObject], delimiter: String) throws {
        let escaped = NSRegularExpression.escapedPatternForString(delimiter)
        let regex = try NSRegularExpression(pattern:"\(escaped)(.*?)\(escaped)", options: [])

        var offset = 0
        regex.enumerateMatchesInString(string, options: [], range: NSRange(location: 0, length: string.characters.count)) { (result, flags, stop) -> Void in
            guard let result = result else {
                return
            }

            let range = NSRange(location: result.range.location + offset, length: result.range.length)
            self.addAttributes(attrs, range: range)
            let replacement = regex.replacementStringForResult(result, inString: self.string, offset: offset, template: "$1")
            self.replaceCharactersInRange(range, withString: replacement)
            offset -= (2 * delimiter.characters.count)
        }
    }
}

Here is how you call it.

let string = NSMutableAttributedString(string:"Here is some $$bold$$ text that should be $$emphasized$$")
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(15)]
try! string.addAttributes(attributes, delimiter: "$$")



回答2:


The iOS way of doing this is with NS[Mutable]AttributedStrings. You set dictionaries of attributes on text ranges. These attributes include font weights, sizes, colors, line spacing, etc.



来源:https://stackoverflow.com/questions/33454310/using-character-delimiters-to-find-and-highlight-text-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!