If I have the following:
let nsRange = NSRange(location: 5, length: 10)
var attributedString = AttributedString("This is some string that I want to attribute.")
// To attribute a substring I must first convert the range
let start = attributedString.index(attributedString.startIndex, offsetByUnicodeScalars: nsRange.location)
let end = attributedString.index(start, offsetByUnicodeScalars: nsRange.length)
// Now we have a Range<AttributedString.Index>
let range = start..<end
attributedString[range].foregroundColor = .red
^ This code works ^
But if I try to remove some boilerplate by creating a subscript as follows:
extension AttributedString {
subscript(_ range: NSRange) -> AttributedSubstring {
get {
let start = index(startIndex, offsetByUnicodeScalars: range.location)
let end = index(start, offsetByUnicodeScalars: range.length)
return self[start..<end]
}
}
}
attributedString[nsRange].foregroundColor = .red
^ this doesn't work ^
I get a Cannot assign through subscript: subscript is get-only
error. But I'm confused why. attributedString[nsRange]
return an AttributedSubstring
, and AttributedSubstring
can have properties set with the .foregroundColor = .red
syntax. It works with a Range<AttributedString.Index>
like str[start..<end].foregroundColor = .red
so why not with my subscript?
CodePudding user response:
You only wrote a getter for the subscript. You also need a setter if you want to write through it (as the error notes):
set {
let start = index(startIndex, offsetByUnicodeScalars: range.location)
let end = index(start, offsetByUnicodeScalars: range.length)
self[start..<end] = newValue
}
The point that Asperi is making is that the returned struct is a copy (this is true in the example you give from the docs as well). These examples are very different:
// This modifies a *copy* of `value`. It does *not* modify `xs`.
// It calls the subscript getter of `xs`.
var value = xs[i]
value.prop = "newThing"
// This modifies `xs`:
// It calls the subscript setter of `xs`.
xs[i].prop = "newThing"
CodePudding user response:
AttributedString
and AttributedSubstring
are structs - ie. value types
let range = start..<end
attributedString[range].foregroundColor = .red
Does not work. this works only if above declaration is var attributedString
- variable, not let attributedString
- constant.
let substring = attributedString[nsRange]
substring.foregroundColor = .red
Does not work. this works only if var substring
- variable
and now
attributedString[nsRange].foregroundColor = .red
this does not work because in-line return of subscript is constant, same as let