Skip to content

Commit 0d21ba9

Browse files
committed
Implement attributesAtIndex(:effectiveRange:)
1 parent a53f339 commit 0d21ba9

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

Foundation/NSAttributedString.swift

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,29 @@ public class NSAttributedString : NSObject, NSCopying, NSMutableCopying, NSSecur
4747
return _string
4848
}
4949

50-
public func attributesAtIndex(_ location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] { NSUnimplemented() }
50+
public func attributesAtIndex(_ location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
51+
var cfRange = CFRange(location: NSNotFound, length: 0)
52+
return withUnsafeMutablePointer(&cfRange) { (rangePointer: UnsafeMutablePointer<CFRange>) -> [String : AnyObject] in
53+
// Get attributes value from `_attributeArray`
54+
let value = CFRunArrayGetValueAtIndex(_attributeArray, location, rangePointer, nil).takeUnretainedValue()
55+
56+
// Convert the value to [String : AnyObject]
57+
let dictionary = unsafeBitCast(value, to: NSDictionary.self)
58+
var results = [String : AnyObject]()
59+
for (key, value) in dictionary {
60+
guard let stringKey = (key as? NSString)?._swiftObject else {
61+
continue
62+
}
63+
results[stringKey] = value
64+
}
65+
66+
// Update effective range
67+
range.pointee.location = rangePointer.pointee.location
68+
range.pointee.length = rangePointer.pointee.length
69+
70+
return results
71+
}
72+
}
5173

5274
public var length: Int {
5375
return _string.length
@@ -64,24 +86,43 @@ public class NSAttributedString : NSObject, NSCopying, NSMutableCopying, NSSecur
6486
public init(string str: String) {
6587
_string = str
6688
_attributeArray = CFRunArrayCreate(kCFAllocatorDefault)
89+
90+
super.init()
91+
addAttributesToAttributeArray(attrs: nil)
6792
}
6893

6994
public init(string str: String, attributes attrs: [String : AnyObject]?) {
7095
_string = str
7196
_attributeArray = CFRunArrayCreate(kCFAllocatorDefault)
7297

73-
let length = _string.length
74-
if (length > 0) {
75-
CFRunArrayInsert(_attributeArray, CFRange(location: 0, length: length), attrs?._cfObject)
76-
}
98+
super.init()
99+
addAttributesToAttributeArray(attrs: attrs)
77100
}
78101

79102
public init(attributedString attrStr: NSAttributedString) { NSUnimplemented() }
80103

104+
private func addAttributesToAttributeArray(attrs: [String : AnyObject]?) {
105+
guard _string.length > 0 else {
106+
return
107+
}
108+
109+
let range = CFRange(location: 0, length: _string.length)
110+
if let attrs = attrs {
111+
CFRunArrayInsert(_attributeArray, range, attrs._cfObject)
112+
} else {
113+
let emptyAttrs = [String : AnyObject]()
114+
CFRunArrayInsert(_attributeArray, range, emptyAttrs._cfObject)
115+
}
116+
}
117+
81118
public func enumerateAttributesInRange(_ enumerationRange: NSRange, options opts: NSAttributedStringEnumerationOptions, usingBlock block: ([String : AnyObject], NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) { NSUnimplemented() }
82119
public func enumerateAttribute(_ attrName: String, inRange enumerationRange: NSRange, options opts: NSAttributedStringEnumerationOptions, usingBlock block: (AnyObject?, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) { NSUnimplemented() }
83120
}
84121

122+
extension NSAttributedString: _CFBridgable {
123+
internal var _cfObject: CFAttributedString { return unsafeBitCast(self, to: CFAttributedString.self) }
124+
}
125+
85126
public struct NSAttributedStringEnumerationOptions : OptionSet {
86127
public let rawValue : UInt
87128
public init(rawValue: UInt) { self.rawValue = rawValue }

TestFoundation/TestNSAttributedString.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class TestNSAttributedString : XCTestCase {
3333
let attrString = NSAttributedString(string: string)
3434
XCTAssertEqual(attrString.string, string)
3535
XCTAssertEqual(attrString.length, string.utf16Count)
36+
37+
var range = NSRange()
38+
let attrs = attrString.attributesAtIndex(0, effectiveRange: &range)
39+
XCTAssertEqual(range.location, NSNotFound)
40+
XCTAssertEqual(range.length, 0)
41+
XCTAssertEqual(attrs.count, 0)
3642
}
3743

3844
func test_initWithStringAndAttributes() {
@@ -42,6 +48,18 @@ class TestNSAttributedString : XCTestCase {
4248
let attrString = NSAttributedString(string: string, attributes: attributes)
4349
XCTAssertEqual(attrString.string, string)
4450
XCTAssertEqual(attrString.length, string.utf16Count)
51+
52+
var range = NSRange()
53+
let attrs = attrString.attributesAtIndex(0, effectiveRange: &range)
54+
55+
guard let value = attrs["attribute.placeholder.key"] as? NSString else {
56+
XCTAssert(false, "attribute value not found")
57+
return
58+
}
59+
60+
XCTAssertEqual(range.location, 0)
61+
XCTAssertEqual(range.length, attrString.length)
62+
XCTAssertEqual(value, "attribute.placeholder.value")
4563
}
4664

4765
}

0 commit comments

Comments
 (0)