Sorry I am still a bit new to swift. I have searched on here but all I could find are outdated answers. How do I construct the AttributedString.Index for insert(_ s: some AttributedStringProtocol, at index: AttributedString.Index)
.
I know I can just add them together but this is just a small piece of what I am actually working on.
func setFooterMessage() -> AttributedString {
var mOne = AttributedString("Play Monday")
mOne.foregroundColor = .white
let mTwo = AttributedString("on")
mOne.insert(mTwo, at: 6) // error
return mOne
}
I’m not entirely sure where the 6
comes from or why there are 3 spaces in "Play Monday"
, as they cause a rather oddly asymmetric output, but the initialisers you need are as follows:
func setFooterMessage() -> AttributedString {
var s: String = "Play Monday"
var mOne = AttributedString(s)
mOne.foregroundColor = .white
let mTwo = AttributedString("on")
// *** You need to get the base index from the `String`
let i = String.Index(utf16Offset: 6, in: s)
// *** and from that you can get the index into the `AttributedString`
if let ia = AttributedString.Index(i, within: mOne) {
// ... but it's optional, as obviously it could fail
mOne.insert(mTwo, at: ia) // no error now
}
return mOne
}
The rationale behind String.Index
as opposed to Int
has been explained in hundreds of answers here and elsewhere, but the overly simplified answer is that in Swift String
s are Unicode sequences, not byte arrays.
AttributedString
s are even more complex in that they have embedded attributes (colours, fonts or even entire images) and so to refer in any meaningful sense to a position in the AttributedString
‘s character sequence, you must do so with reference to the equivalent String
.
String
and AttributedString
don’t work with Int
indices because characters can have more than one byte.
In case of AttributedString
you can use the API index(_:offsetByCharacters:)
which calculates the proper index
func setFooterMessage() -> AttributedString {
var mOne = AttributedString("Play Monday")
mOne.foregroundColor = .white
let mTwo = AttributedString("on")
let index = mOne.index(mOne.startIndex, offsetByCharacters: 6) // actually 5
mOne.insert(mTwo, at: index)
return mOne
}
Another way is to get the index by the range of “Play ” and insert the substring at its upperbound
func setFooterMessage() -> AttributedString {
var mOne = AttributedString("Play Monday")
mOne.foregroundColor = .white
let mTwo = AttributedString("on")
let index = mOne.range(of: "Play ")!
mOne.insert(mTwo, at: index.upperBound)
return mOne
}
Related: How to remove a substring from string of particular index to some length in swift