问题
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]AttributedString
s. 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