问题
In response to an example I posted previously, Andreas Oetjen showed a neat way to display a table of fractional and decimal numbers in textView using attributed strings aligning numbers so the decimal point or forward slash characters appear in the same column as shown in Example 1 below. With that introduction to attributed strings I was prompted to ask: how to display each column in a different colour or font ? (to associate data with file types found elsewhere in the app)
When I try to do this the closest I get is the display shown in Example 2 below and I can’t figure out why the text is not displayed in two columns, one in blue, the other in red with numbers in the red column aligned the same as in Example 1. Example 2 has two issues. Firstly, paragraph formatting of Example 1 is lost when font formatting is introduced and secondly, there are two versions of the text, one in colour, the other in black. I suspect both issues have the same underlying cause.
Based on Andreas' answer, the first code example below assembles strings that make up each line and then uses NSMutableParagraphStyle()
to create the layout shown in Example 1 (above). The second code example applies formatting to the strings before appending formatted strings in each line - my attempt to interleave NSMutableParagraphStyle()
and NSMutableAttributedString
- to create the layout shown in Example 2 (above). Both were tested in Playground. With the second example, I tried switching the order of calling the formatting functions e.g.
appendLeftColumnParagraph(textIn: column1)
appendLeftColumnFont(i: i)
and
appendRightColumnParagraph(textIn: column2)
appendRightColumnFont(i: i)
instead of
appendLeftColumnFont(i: i)
appendLeftColumnParagraph(textIn: column1)
and
appendRightColumnFont(i: i)
appendRightColumnParagraph(textIn: column2)
but this only makes the coloured characters appear after the black characters instead of before. It's almost as if I can only expect to append an attributed string using either NSMutableAttributedString
or NSMutableParagraphStyle()
but not both. Either that or there's something basic I am not understanding about appending attributed strings.
So, assuming this is the right question, how do I interleave NSMutableParagraphStyle()
and NSMutableAttributedString
to format a string displayed in UITextView
as two columns, one in blue, the other in red with numbers in the red column aligned the same as in Example 1 ?
I’d be grateful if someone with some fresh brain cells can help me answer this. Thanks.
Code example 1 - with NSMutableParagraphStyle() only
import UIKit
import PlaygroundSupport
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
let scaleSize = tuningArray.count
var textMessage = ""
for i in 0..<scaleSize {
var textLine = "\t" + keyArray[i] + "\t" + tuningArray[i] + "\n"
textMessage += textLine
}
let paragraphStyle = NSMutableParagraphStyle()
let leftTabLocation = CGFloat(50)
let rightTabLocation = CGFloat(185)
var leftTab = NSTextTab(textAlignment: .left, location: leftTabLocation, options: [:])
let rightTerminators:CharacterSet = [".", "/"]
let rightTabOptions = [NSTabColumnTerminatorsAttributeName:rightTerminators]
var rightTab = NSTextTab(textAlignment: .natural, location: rightTabLocation, options:rightTabOptions)
paragraphStyle.tabStops = [leftTab, rightTab]
var attributedText = NSAttributedString(string:textMessage,attributes: [NSParagraphStyleAttributeName: paragraphStyle])
let formattedText = UITextView(frame:CGRect(x:0, y:0, width:300, height:200))
formattedText.attributedText = attributedText
PlaygroundPage.current.liveView = formattedText
Code example 2 - NSMutableParagraphStyle() with NSMutableAttributedString.init(string: )
import UIKit
import PlaygroundSupport
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
let scaleSize = tuningArray.count
var textMessage = ""
var textString = String()
var formattedString = NSAttributedString()
var rangeOfKeyString = NSRange()
var attributeKey = NSMutableAttributedString()
var rangeOfTuningString = NSRange()
var attributeTuning = NSMutableAttributedString()
let attributedTextMessage = NSMutableAttributedString()
func appendLeftColumnParagraph(textIn: String) {
let paragraphStyle = NSMutableParagraphStyle()
let leftTabLocation = CGFloat(50.0)
let leftTab = NSTextTab(textAlignment: .left, location: leftTabLocation, options: [:])
paragraphStyle.tabStops = [leftTab]
let attributedLeftColumnParagraph = NSAttributedString(string:textIn, attributes: [NSParagraphStyleAttributeName: paragraphStyle])
attributedTextMessage.append(attributedLeftColumnParagraph)
}
func appendRightColumnParagraph(textIn: String) {
let paragraphStyle = NSMutableParagraphStyle()
let rightTabLocation = CGFloat(185.0)
let rightSeparators:CharacterSet = [".", "/"]
let rightTabOptions = [NSTabColumnTerminatorsAttributeName: rightSeparators]
let rightTab = NSTextTab(textAlignment: .natural, location: rightTabLocation, options: rightTabOptions)
paragraphStyle.tabStops = [rightTab]
let attributedRightColumnParagraph = NSAttributedString(string:textIn, attributes: [NSParagraphStyleAttributeName: paragraphStyle])
attributedTextMessage.append(attributedRightColumnParagraph)
}
func appendLeftColumnFont(i: Int){
rangeOfKeyString = (keyArray[i] as NSString).range(of: keyArray[i])
attributeKey = NSMutableAttributedString.init(string: keyArray[i])
attributeKey.addAttribute(NSForegroundColorAttributeName, value: UIColor.blue, range: rangeOfKeyString)
attributedTextMessage.append(attributeKey)
}
func appendRightColumnFont(i: Int){
rangeOfTuningString = (tuningArray[i] as NSString).range(of: tuningArray[i])
attributeTuning = NSMutableAttributedString.init(string: tuningArray[i])
attributeTuning.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: rangeOfTuningString)
attributedTextMessage.append(attributeTuning)
}
func appendTab(){
let attributedTab = NSMutableAttributedString.init(string: "\t")
attributedTextMessage.append(attributedTab)
}
func appendNewLine(){
let attributedNewLine = NSMutableAttributedString.init(string: "\n")
attributedTextMessage.append(attributedNewLine)
}
for i in 0..<scaleSize {
// var textLine = "\t" + keyArray[i] + "\t" + tuningArray[i] + "\n"
let column1 = keyArray[i]
let column2 = tuningArray[i]
appendTab()
appendLeftColumnFont(i: i)
appendLeftColumnParagraph(textIn: column1)
appendTab()
appendRightColumnFont(i: i)
appendRightColumnParagraph(textIn: column2)
appendNewLine()
}
formattedString = attributedTextMessage
let formattedText = UITextView(frame:CGRect(x:0, y:0, width:300, height:200))
formattedText.attributedText = formattedString
PlaygroundPage.current.liveView = formattedText
回答1:
You'll have to add the paragraph style to each and every attributed string that you append (e.g. also to the new line and tab characters); the easiest way would be to add the style at the very end, to the full range:
import UIKit
import PlaygroundSupport
var tuningArray = ["1/1", "76.04900", "193.15686", "310.26471", "5/4", "503.42157", "579.47057", "696.57843", "25/16", "889.73529", "1006.84314", "1082.89214"]
var keyArray = ["x", "1", "x", "x", "4", "x", "6", "x", "x", "9", "x", "11"]
let scaleSize = tuningArray.count
var textMessage = ""
var textString = String()
var formattedString = NSAttributedString()
var rangeOfKeyString = NSRange()
var attributeKey = NSMutableAttributedString()
var rangeOfTuningString = NSRange()
var attributeTuning = NSMutableAttributedString()
let attributedTextMessage = NSMutableAttributedString()
func getParagraphStyles() -> NSMutableParagraphStyle {
let paragraphStyle = NSMutableParagraphStyle()
let leftTabLocation = CGFloat(50.0)
let leftTab = NSTextTab(textAlignment: .left, location: leftTabLocation, options: [:])
let rightTabLocation = CGFloat(185.0)
let rightSeparators:CharacterSet = [".", "/"]
let rightTabOptions = [NSTabColumnTerminatorsAttributeName: rightSeparators]
let rightTab = NSTextTab(textAlignment: .natural, location: rightTabLocation, options: rightTabOptions)
paragraphStyle.tabStops = [leftTab, rightTab]
return paragraphStyle
}
func appendLeftColumnFont(i: Int){
rangeOfKeyString = (keyArray[i] as NSString).range(of: keyArray[i])
attributeKey = NSMutableAttributedString.init(string: keyArray[i])
attributeKey.addAttribute(NSForegroundColorAttributeName, value: UIColor.blue, range: rangeOfKeyString)
attributedTextMessage.append(attributeKey)
}
func appendRightColumnFont(i: Int){
rangeOfTuningString = (tuningArray[i] as NSString).range(of: tuningArray[i])
attributeTuning = NSMutableAttributedString.init(string: tuningArray[i])
attributeTuning.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: rangeOfTuningString)
attributedTextMessage.append(attributeTuning)
}
func appendTab(){
let attributedTab = NSMutableAttributedString.init(string: "\t")
attributedTextMessage.append(attributedTab)
}
func appendNewLine(){
let attributedNewLine = NSMutableAttributedString.init(string: "\n")
attributedTextMessage.append(attributedNewLine)
}
for i in 0..<scaleSize {
// var textLine = "\t" + keyArray[i] + "\t" + tuningArray[i] + "\n"
let column1 = keyArray[i]
let column2 = tuningArray[i]
appendTab()
appendLeftColumnFont(i: i)
appendTab()
appendRightColumnFont(i: i)
appendNewLine()
}
// Apply paragraph to whole string:
var paragraphStyle = getParagraphStyles()
let fullRange = NSRange(location: 0, length: attributedTextMessage.length)
attributedTextMessage.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: fullRange)
formattedString = attributedTextMessage
let formattedText = UITextView(frame:CGRect(x:0, y:0, width:300, height:200))
formattedText.attributedText = formattedString
PlaygroundPage.current.liveView = formattedText
来源:https://stackoverflow.com/questions/42298647/in-swift-how-do-i-interleave-nsmutableparagraphstyle-and-nsmutableattributedst