如何使用 TextKit(不是 NSAttributedStrings)在 UITextView 中获取属性信息
how to get Attributed-Informations in UITextView with TextKit (not NSAttributedStrings)
我有一个 UITextView 和带有 UITextKit 样式 (NSStrikethroughStyleAttributeName) 的 TextKit:
这是我的代码:
@IBOutlet weak var textView: UITextView!
var dict = [String: AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let newFont = UIFont(name:"HelveticaNeue", size: textView.font!.pointSize)
self.textView.font = newFont
dict[NSFontAttributeName] = newFont
let selectedRange: NSRange = NSMakeRange(12,6)
self.makeStrikeThrough(selectedRange)
}
func makeStrikeThrough(selectedRange: NSRange) {
dict[NSStrikethroughStyleAttributeName] = 2
self.textView.textStorage.beginEditing()
self.textView.textStorage.setAttributes(dict, range: selectedRange)
self.textView.textStorage.endEditing()
}
现在我必须有办法检测这个字体属性。有什么方法可以获取信息:
在 selectedRange: NSRange 12, 6 我使用属性 NSStrikethroughStyleAttributeName 和 属性 2,也许作为数组项???
欢迎任何想法!
唯一属性范围的离散化(w.r.t。left/right 这些的相邻范围)
您可以重复使用 NSAttributedString
的 attributesAtIndex(location:effectiveRange:)
方法,将整个属性字符串的范围编码为子范围列表,每个子范围包含一组属性和这些值。
Declaration:
func attributesAtIndex(location: Int,
effectiveRange range: NSRangePointer) -> [String : AnyObject]
Description:
Returns the attributes for the character at a given index.
Return value:
The attributes for the character at index
.
更具体地说,使用 attributesAtIndex(...)
创建 NSAttributedString
的扩展 return 和元组数组,元组定义为
- 第一个元组元素是一组不同属性(或缺少)的范围。
- 第二个元组元素本身就是一个元组数组,这些元组定义为:
- 第一个子元组元素是属性名称。
- 第二个子元组元素是属性值。
- 或者,第二个元组元素是一个
[String: AnyObject]
数组,对应于属性及其各自的值(两种变体都包含在下面)
属性字符串中由多个属性赋予的范围自然会 return 几个元素的内部元组数组,而根本没有属性的范围将 return 一个空的内部元组数组.
分机(二选一)如下:
/* let 2nd tuple be an array of tuples itself */
extension NSAttributedString {
func getAttributes() -> [(NSRange, [(String, AnyObject)])] {
var attributesOverRanges : [(NSRange, [(String, AnyObject)])] = []
var rng = NSRange()
var idx = 0
while idx < self.length {
let foo = self.attributesAtIndex(idx, effectiveRange: &rng)
var attributes : [(String, AnyObject)] = []
for (k, v) in foo { attributes.append(k, v) }
attributesOverRanges.append((rng, attributes))
idx = max(idx + 1, rng.toRange()?.endIndex ?? 0)
}
return attributesOverRanges
}
}
/* or, let 2nd tuple be a [String: AnyObject] dictionary */
extension NSAttributedString {
func getAttributes() -> [(NSRange, [String: AnyObject])] {
var attributesOverRanges : [(NSRange, [String: AnyObject])] = []
var rng = NSRange()
var idx = 0
while idx < self.length {
let foo = self.attributesAtIndex(idx, effectiveRange: &rng)
attributesOverRanges.append((rng, foo))
idx = max(idx + 1, rng.toRange()?.endIndex ?? 0)
}
return attributesOverRanges
}
}
用法示例:
/* Example setup */
let fooString = "foo foo foo foo foo foo foo"
var fooAttrString = NSMutableAttributedString(string: fooString)
let selectedRange: NSRange = NSMakeRange(12,6)
// attr1: strikethrough over range (12,6) (12..<18)
var myRange = NSRange(location: 12, length: 6)
let strikeThroughAttr = [ NSStrikethroughStyleAttributeName: 2 ]
fooAttrString.addAttributes(strikeThroughAttr, range: myRange)
// attr2: font over range (16,8) (16..<24)
myRange = NSRange(location: 16, length: 8)
let fontAttr = [ NSFontAttributeName: UIFont(name:"HelveticaNeue", size: 20)! ]
fooAttrString.addAttributes(fontAttr, range: myRange)
/* Example usage: extension */
let attributesOverRanges = fooAttrString.getAttributes()
for (rng, attributes) in attributesOverRanges {
print("Attributes over range \(rng):")
attributes.forEach { print("\t\([=12=].0) = \([=12=].1)") }
}
/* Attributes over range (0,12):
Attributes over range (12,4):
NSStrikethrough = 2
Attributes over range (16,2):
NSFont = <UICTFont: 0x7fcfd860f0b0> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
NSStrikethrough = 2
Attributes over range (18,6):
NSFont = <UICTFont: 0x7fcfd860f0b0> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
Attributes over range (24,3): */
将上述应用到您的 UITextView
实例,特别是 属性 textStorage
现在,UITextView
的 textStorage
属性 是 NSTextStorage
类型,它是 NSMutableAttributedString
的(半具体)子类,它本身是 NSAttributedString
的子类。因此,上面的扩展 getAttributes()
将可以访问,并且在 NSTextStorage
个实例上也能正常工作,例如textView.textStorage
在你的问题中。
因此,使用与上述相同的扩展,我们设置了一个类似的示例,但是对于具有属性 textStorage
属性.
的 UITextView
/* Example setup: UITextView:s 'textStorage' (type NSTextStorage) */
let fooString = "foo foo foo foo foo foo foo"
// attr1: strikethrough over range (12,6) (12..<18)
let strikeThroughRng = NSRange(location: 12, length: 6)
let strikeThroughAttr = [ NSStrikethroughStyleAttributeName: 2 ]
// attr2: font over range (16,8) (16..<24)
let fontRng = NSRange(location: 16, length: 8)
let fontAttr = [ NSFontAttributeName: UIFont(name:"HelveticaNeue", size: 20)! ]
// create text view and set attributes
let textView = UITextView()
textView.text = fooString
textView.textStorage.beginEditing()
textView.textStorage.addAttributes(strikeThroughAttr, range: strikeThroughRng)
textView.textStorage.addAttributes(fontAttr, range: fontRng)
textView.textStorage.endEditing()
用法示例,扩展名:
/* Example usage: extension (uses first version above) */
let attributesOverRanges = textView.textStorage.getAttributes()
for (rng, attributes) in attributesOverRanges {
print("Attributes over range \(rng):")
attributes.forEach { print("\t\([=14=].0) = \([=14=].1)") }
}
/* Attributes over range (0,12):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
Attributes over range (12,4):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSStrikethrough = 2
Attributes over range (16,2):
NSFont = <UICTFont: 0x7ff610d80820> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
NSStrikethrough = 2
Attributes over range (18,6):
NSFont = <UICTFont: 0x7ff610d80820> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
Attributes over range (24,3):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt */
正如预期的那样,我们看到与上面 NSAttributedString
示例相同的结果,区别在于 textView.textStorage
包含一些默认属性(NSFont
、NSOriginalFont
).
在属性字符串中搜索第一次出现的给定属性和属性值
如果您愿意,您还可以编写一个扩展来搜索属性字符串以查找特定属性和值,使用 NSAttributedString
的 attribute(attrName:atIndex:effectiveRange:)
方法
Declaration:
func attribute(attrName: String, atIndex location: Int,
effectiveRange range: NSRangePointer) -> AnyObject?
Description:
Returns the value for an attribute with a given name of the character
at a given index, and by reference the range over which the attribute
applies.
Return value:
The value for the attribute named attributeName
of the character at
index
, or nil
if there is no such attribute.
更具体地说,创建一个扩展
- 为给定值(例如
2
)搜索给定属性(例如 NSFontAttributeName
)的属性字符串,并且 return 范围(NSRange
)对于给定属性字符串中此类属性部分的第一次出现,或 nil
,如果可以找到 none。
NSAttributedString
扩展如下
/* find the range of (the first occurence of) a given
attribute 'attrName' for a given value 'forValue'. */
extension NSAttributedString {
func findRangeOfAttribute(attrName: String, forValue value: AnyObject) -> NSRange? {
var rng = NSRange()
/* Is attribute (with given value) in range 0...X ? */
if let val = self.attribute(attrName, atIndex: 0, effectiveRange: &rng) where val.isEqual(value) { return rng }
/* If not, is attribute (with given value) anywhere in range X+1..<end? */
else if
let from = rng.toRange()?.endIndex where from < self.length - 1,
let val = self.attribute(attrName, atIndex: from, effectiveRange: &rng) where val.isEqual(value) { return rng }
/* if none of the above, return nil */
return nil
}
}
用法示例:
/* Example */
let fooString = "foo foo foo foo foo foo foo"
var fooAttrString = NSMutableAttributedString(string: fooString)
let selectedRange: NSRange = NSMakeRange(12,6)
let myRange = NSRange(location: 12, length: 6)
let attr = [ NSStrikethroughStyleAttributeName: 2 ]
fooAttrString.addAttributes(attr, range: myRange)
/* Example usage: extension */
if let rngOfFirstStrikethrough = fooAttrString.findRangeOfAttribute(NSStrikethroughStyleAttributeName, forValue: 2) {
print(rngOfStrikethrough) // (12,6)
}
我有一个 UITextView 和带有 UITextKit 样式 (NSStrikethroughStyleAttributeName) 的 TextKit:
这是我的代码:
@IBOutlet weak var textView: UITextView!
var dict = [String: AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let newFont = UIFont(name:"HelveticaNeue", size: textView.font!.pointSize)
self.textView.font = newFont
dict[NSFontAttributeName] = newFont
let selectedRange: NSRange = NSMakeRange(12,6)
self.makeStrikeThrough(selectedRange)
}
func makeStrikeThrough(selectedRange: NSRange) {
dict[NSStrikethroughStyleAttributeName] = 2
self.textView.textStorage.beginEditing()
self.textView.textStorage.setAttributes(dict, range: selectedRange)
self.textView.textStorage.endEditing()
}
现在我必须有办法检测这个字体属性。有什么方法可以获取信息:
在 selectedRange: NSRange 12, 6 我使用属性 NSStrikethroughStyleAttributeName 和 属性 2,也许作为数组项???
欢迎任何想法!
唯一属性范围的离散化(w.r.t。left/right 这些的相邻范围)
您可以重复使用 NSAttributedString
的 attributesAtIndex(location:effectiveRange:)
方法,将整个属性字符串的范围编码为子范围列表,每个子范围包含一组属性和这些值。
Declaration:
func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject]
Description:
Returns the attributes for the character at a given index.
Return value:
The attributes for the character at
index
.
更具体地说,使用 attributesAtIndex(...)
创建 NSAttributedString
的扩展 return 和元组数组,元组定义为
- 第一个元组元素是一组不同属性(或缺少)的范围。
- 第二个元组元素本身就是一个元组数组,这些元组定义为:
- 第一个子元组元素是属性名称。
- 第二个子元组元素是属性值。
- 或者,第二个元组元素是一个
[String: AnyObject]
数组,对应于属性及其各自的值(两种变体都包含在下面)
属性字符串中由多个属性赋予的范围自然会 return 几个元素的内部元组数组,而根本没有属性的范围将 return 一个空的内部元组数组.
分机(二选一)如下:
/* let 2nd tuple be an array of tuples itself */
extension NSAttributedString {
func getAttributes() -> [(NSRange, [(String, AnyObject)])] {
var attributesOverRanges : [(NSRange, [(String, AnyObject)])] = []
var rng = NSRange()
var idx = 0
while idx < self.length {
let foo = self.attributesAtIndex(idx, effectiveRange: &rng)
var attributes : [(String, AnyObject)] = []
for (k, v) in foo { attributes.append(k, v) }
attributesOverRanges.append((rng, attributes))
idx = max(idx + 1, rng.toRange()?.endIndex ?? 0)
}
return attributesOverRanges
}
}
/* or, let 2nd tuple be a [String: AnyObject] dictionary */
extension NSAttributedString {
func getAttributes() -> [(NSRange, [String: AnyObject])] {
var attributesOverRanges : [(NSRange, [String: AnyObject])] = []
var rng = NSRange()
var idx = 0
while idx < self.length {
let foo = self.attributesAtIndex(idx, effectiveRange: &rng)
attributesOverRanges.append((rng, foo))
idx = max(idx + 1, rng.toRange()?.endIndex ?? 0)
}
return attributesOverRanges
}
}
用法示例:
/* Example setup */
let fooString = "foo foo foo foo foo foo foo"
var fooAttrString = NSMutableAttributedString(string: fooString)
let selectedRange: NSRange = NSMakeRange(12,6)
// attr1: strikethrough over range (12,6) (12..<18)
var myRange = NSRange(location: 12, length: 6)
let strikeThroughAttr = [ NSStrikethroughStyleAttributeName: 2 ]
fooAttrString.addAttributes(strikeThroughAttr, range: myRange)
// attr2: font over range (16,8) (16..<24)
myRange = NSRange(location: 16, length: 8)
let fontAttr = [ NSFontAttributeName: UIFont(name:"HelveticaNeue", size: 20)! ]
fooAttrString.addAttributes(fontAttr, range: myRange)
/* Example usage: extension */
let attributesOverRanges = fooAttrString.getAttributes()
for (rng, attributes) in attributesOverRanges {
print("Attributes over range \(rng):")
attributes.forEach { print("\t\([=12=].0) = \([=12=].1)") }
}
/* Attributes over range (0,12):
Attributes over range (12,4):
NSStrikethrough = 2
Attributes over range (16,2):
NSFont = <UICTFont: 0x7fcfd860f0b0> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
NSStrikethrough = 2
Attributes over range (18,6):
NSFont = <UICTFont: 0x7fcfd860f0b0> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
Attributes over range (24,3): */
将上述应用到您的 UITextView
实例,特别是 属性 textStorage
现在,UITextView
的 textStorage
属性 是 NSTextStorage
类型,它是 NSMutableAttributedString
的(半具体)子类,它本身是 NSAttributedString
的子类。因此,上面的扩展 getAttributes()
将可以访问,并且在 NSTextStorage
个实例上也能正常工作,例如textView.textStorage
在你的问题中。
因此,使用与上述相同的扩展,我们设置了一个类似的示例,但是对于具有属性 textStorage
属性.
UITextView
/* Example setup: UITextView:s 'textStorage' (type NSTextStorage) */
let fooString = "foo foo foo foo foo foo foo"
// attr1: strikethrough over range (12,6) (12..<18)
let strikeThroughRng = NSRange(location: 12, length: 6)
let strikeThroughAttr = [ NSStrikethroughStyleAttributeName: 2 ]
// attr2: font over range (16,8) (16..<24)
let fontRng = NSRange(location: 16, length: 8)
let fontAttr = [ NSFontAttributeName: UIFont(name:"HelveticaNeue", size: 20)! ]
// create text view and set attributes
let textView = UITextView()
textView.text = fooString
textView.textStorage.beginEditing()
textView.textStorage.addAttributes(strikeThroughAttr, range: strikeThroughRng)
textView.textStorage.addAttributes(fontAttr, range: fontRng)
textView.textStorage.endEditing()
用法示例,扩展名:
/* Example usage: extension (uses first version above) */
let attributesOverRanges = textView.textStorage.getAttributes()
for (rng, attributes) in attributesOverRanges {
print("Attributes over range \(rng):")
attributes.forEach { print("\t\([=14=].0) = \([=14=].1)") }
}
/* Attributes over range (0,12):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
Attributes over range (12,4):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSStrikethrough = 2
Attributes over range (16,2):
NSFont = <UICTFont: 0x7ff610d80820> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
NSStrikethrough = 2
Attributes over range (18,6):
NSFont = <UICTFont: 0x7ff610d80820> font-family: "Helvetica Neue"; font-weight: normal; font-style: normal; font-size: 20.00pt
Attributes over range (24,3):
NSOriginalFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt
NSFont = <UICTFont: 0x7ff610d88e20> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt */
正如预期的那样,我们看到与上面 NSAttributedString
示例相同的结果,区别在于 textView.textStorage
包含一些默认属性(NSFont
、NSOriginalFont
).
在属性字符串中搜索第一次出现的给定属性和属性值
如果您愿意,您还可以编写一个扩展来搜索属性字符串以查找特定属性和值,使用 NSAttributedString
attribute(attrName:atIndex:effectiveRange:)
方法
Declaration:
func attribute(attrName: String, atIndex location: Int, effectiveRange range: NSRangePointer) -> AnyObject?
Description:
Returns the value for an attribute with a given name of the character at a given index, and by reference the range over which the attribute applies.
Return value:
The value for the attribute named
attributeName
of the character atindex
, ornil
if there is no such attribute.
更具体地说,创建一个扩展
- 为给定值(例如
2
)搜索给定属性(例如NSFontAttributeName
)的属性字符串,并且 return 范围(NSRange
)对于给定属性字符串中此类属性部分的第一次出现,或nil
,如果可以找到 none。
NSAttributedString
扩展如下
/* find the range of (the first occurence of) a given
attribute 'attrName' for a given value 'forValue'. */
extension NSAttributedString {
func findRangeOfAttribute(attrName: String, forValue value: AnyObject) -> NSRange? {
var rng = NSRange()
/* Is attribute (with given value) in range 0...X ? */
if let val = self.attribute(attrName, atIndex: 0, effectiveRange: &rng) where val.isEqual(value) { return rng }
/* If not, is attribute (with given value) anywhere in range X+1..<end? */
else if
let from = rng.toRange()?.endIndex where from < self.length - 1,
let val = self.attribute(attrName, atIndex: from, effectiveRange: &rng) where val.isEqual(value) { return rng }
/* if none of the above, return nil */
return nil
}
}
用法示例:
/* Example */
let fooString = "foo foo foo foo foo foo foo"
var fooAttrString = NSMutableAttributedString(string: fooString)
let selectedRange: NSRange = NSMakeRange(12,6)
let myRange = NSRange(location: 12, length: 6)
let attr = [ NSStrikethroughStyleAttributeName: 2 ]
fooAttrString.addAttributes(attr, range: myRange)
/* Example usage: extension */
if let rngOfFirstStrikethrough = fooAttrString.findRangeOfAttribute(NSStrikethroughStyleAttributeName, forValue: 2) {
print(rngOfStrikethrough) // (12,6)
}