How can I concatenate NSAttributedStrings?
Asked Answered
G

9

188

I need to search some strings and set some attributes prior to merging the strings, so having NSStrings -> Concatenate them -> Make NSAttributedString is not an option, is there any way to concatenate attributedString to another attributedString?

Govea answered 29/8, 2013 at 18:15 Comment(17)
It is ridiculous how difficult this still is in August of 2016.Combust
Even in 2018...Tiki
still in 2019 ;)Dissymmetry
still in 2020 ...Brenza
Still in November 2020 ;)Haro
Ah... two weeks before 2021! Merry Christmas all of us ;)Kirby
Hopefully things change in 2021 ¯\_(ツ)_/¯Childe
Still in 2021 MarchNostology
Still 2021 April hahhaTharp
Still 2021 June XDGraduation
This really is a joke! (Aug 2021)Telles
November 2021, alright this is ridiculous!Annulate
March 2022, the tradition remainsFornof
August 2022, yet... xDTriaxial
Mandatory Check In - Nov'2022 it continues..... xDLayoff
and still in Mar 2023Delorisdelorme
Happy and hopeful 2024Kerrikerrie
M
223

I'd recommend you use a single mutable attributed string a @Linuxios suggested, and here's another example of that:

NSMutableAttributedString *mutableAttString = [[NSMutableAttributedString alloc] init];

NSString *plainString = // ...
NSDictionary *attributes = // ... a dictionary with your attributes.
NSAttributedString *newAttString = [[NSAttributedString alloc] initWithString:plainString attributes:attributes];

[mutableAttString appendAttributedString:newAttString];

However, just for the sake of getting all the options out there, you could also create a single mutable attributed string, made from a formatted NSString containing the input strings already put together. You could then use addAttributes: range: to add the attributes after the fact to the ranges containing the input strings. I recommend the former way though.

Moonseed answered 29/8, 2013 at 18:22 Comment(1)
Why do you recommend appending strings instead of adding attributes?Housebreak
P
108

If you're using Swift, you can just overload the + operator so that you can concatenate them in the same way you concatenate normal strings:

// concatenate attributed strings
func + (left: NSAttributedString, right: NSAttributedString) -> NSAttributedString
{
    let result = NSMutableAttributedString()
    result.append(left)
    result.append(right)
    return result
}

Now you can concatenate them just by adding them:

let helloworld = NSAttributedString(string: "Hello ") + NSAttributedString(string: "World")
Pomp answered 19/9, 2015 at 15:37 Comment(9)
but result is mutable! it doesn't make sense for types!Camisado
the mutable class is a subtype of the immutable class.Pomp
so I can cast result to mutable counterpart and get no error?Camisado
You can use the mutable subtype in any context that expects the immutable parent type but not vice versa. You may want to review subclassing and inheritance.Pomp
it was sarcasm. again, you should not return mutable subtype in immutable parent context in case of security. It doesn't make sense that you return 'hidden' mutable object in immutable context. Please, review immutable/mutable contexts and purposes of each oneCamisado
Yes, you should do a defensive copy if you want to be defensive. (Not sarcasm.)Pomp
If you really want to return NSAttributedString, then perhaps this would work: return NSAttributedString(attributedString: result)Mutilate
Swift 3: result.append(left) - where do you declare such an overload? Is there a standard place where to do it? Seems weird if it's just randomly in the file you just happen to be in...Jeanette
@Jeanette I would create a folder called Helpers or Extensions and put this function in a file named NSAttributedString+Concatenate.swift.Hortatory
F
43

Swift 3: Simply create a NSMutableAttributedString and append the attributed strings to them.

let mutableAttributedString = NSMutableAttributedString()

let boldAttribute = [
    NSFontAttributeName: UIFont(name: "GothamPro-Medium", size: 13)!,
    NSForegroundColorAttributeName: Constants.defaultBlackColor
]

let regularAttribute = [
    NSFontAttributeName: UIFont(name: "Gotham Pro", size: 13)!,
    NSForegroundColorAttributeName: Constants.defaultBlackColor
]

let boldAttributedString = NSAttributedString(string: "Warning: ", attributes: boldAttribute)
let regularAttributedString = NSAttributedString(string: "All tasks within this project will be deleted.  If you're sure you want to delete all tasks and this project, type DELETE to confirm.", attributes: regularAttribute)
mutableAttributedString.append(boldAttributedString)
mutableAttributedString.append(regularAttributedString)

descriptionTextView.attributedText = mutableAttributedString

swift5 upd:

    let captionAttribute = [
        NSAttributedString.Key.font: Font.captionsRegular,
        NSAttributedString.Key.foregroundColor: UIColor.appGray
    ]
Farris answered 14/6, 2017 at 17:53 Comment(0)
P
28

Try this:

NSMutableAttributedString* result = [astring1 mutableCopy];
[result appendAttributedString:astring2];

Where astring1 and astring2 are NSAttributedStrings.

Providenciaprovident answered 29/8, 2013 at 18:18 Comment(10)
Or [[aString1 mutableCopy] appendAttributedString: aString2].Fireproofing
@Fireproofing your 'oneliner' is corrupted. you can't get this "concatenation" result because appendAttributedString doesn't return string. Same story with dictionariesCamisado
@gaussblurinc: good point, of course your criticism also applies to the answer we're commenting on. It should be NSMutableAttributedString* aString3 = [aString1 mutableCopy]; [aString3 appendAttributedString: aString2];.Fireproofing
@gaussblurinc, JWalker: Corrected the answer.Providenciaprovident
@Linuxios, also, you return result as NSMutableAttributedString. it is not what author want to see. stringByAppendingString - this method will be goodCamisado
@gaussblurinc: The original post isn't clear enough for us to know that, and I don't really see a problem with this method.Providenciaprovident
@Linuxios, concatenate attributedStrings to another attributedString <- no word about mutable or something else. Also, author said about merge two strings to get NSAttributedString ( not an option in case of lost attributes ). So, concatenate attributesStrings and get NSAttributedStringCamisado
@gaussblurinc: Unless I'm misunderstanding inheritance in Obj-c, a mutable attributed string can be used anywhere a normal one can.Providenciaprovident
@Providenciaprovident this means that your approach has potential problems in security/stability. Immutable objects are better, because they can't be changed by third-party person. For example, all properties of NSString type should be have copy attribute, because they should not be changed by anyone who has a pointer to them.Camisado
Let us continue this discussion in chat.Providenciaprovident
L
17

2020 | SWIFT 5.1:

You're able to add 2 NSMutableAttributedString by the following way:

let concatenated = NSAttrStr1.append(NSAttrStr2)

Another way works with NSMutableAttributedString and NSAttributedString both:

[NSAttrStr1, NSAttrStr2].joinWith(separator: "")

Another way is....

var full = NSAttrStr1 + NSAttrStr2 + NSAttrStr3

and:

var full = NSMutableAttributedString(string: "hello ")
// NSAttrStr1 == 1


full += NSAttrStr1 // "hello 1"       
full += " world"   // "hello 1 world"

You can do this with the following extension:

// works with NSAttributedString and NSMutableAttributedString!
public extension NSAttributedString {
    static func + (left: NSAttributedString, right: NSAttributedString) -> NSAttributedString {
        let leftCopy = NSMutableAttributedString(attributedString: left)
        leftCopy.append(right)
        return leftCopy
    }
    
    static func + (left: NSAttributedString, right: String) -> NSAttributedString {
        let leftCopy = NSMutableAttributedString(attributedString: left)
        let rightAttr = NSMutableAttributedString(string: right)
        leftCopy.append(rightAttr)
        return leftCopy
    }
    
    static func + (left: String, right: NSAttributedString) -> NSAttributedString {
        let leftAttr = NSMutableAttributedString(string: left)
        leftAttr.append(right)
        return leftAttr
    }
}

public extension NSMutableAttributedString {
    static func += (left: NSMutableAttributedString, right: String) -> NSMutableAttributedString {
        let rightAttr = NSMutableAttributedString(string: right)
        left.append(rightAttr)
        return left
    }
    
    static func += (left: NSMutableAttributedString, right: NSAttributedString) -> NSMutableAttributedString {
        left.append(right)
        return left
    }
}
Luannaluanne answered 20/2, 2020 at 22:16 Comment(4)
I'm using Swift 5.1 and I can't seem to just add two NSAttrStrings together...Sweetie
Strange. In this case just use NSAttrStr1.append(NSAttrStr2)Luannaluanne
Updated my answer with extensions for just adding two NSAttrStrings :)Luannaluanne
Using Swift 5 joinWith didn't seem to work...ended up using append method.Layoff
T
4

If you're using Cocoapods, an alternative to both above answers that let you avoid mutability in your own code is to use the excellent NSAttributedString+CCLFormat category on NSAttributedStrings that lets you write something like:

NSAttributedString *first = ...;
NSAttributedString *second = ...;
NSAttributedString *combined = [NSAttributedString attributedStringWithFormat:@"%@%@", first, second];

It of course it just uses NSMutableAttributedString under the covers.

It also has the extra advantage of being a fully fledged formatting function — so it can do a lot more than appending strings together.

Trapan answered 17/7, 2014 at 11:45 Comment(0)
C
1
// Immutable approach
// class method

+ (NSAttributedString *)stringByAppendingString:(NSAttributedString *)append toString:(NSAttributedString *)string {
  NSMutableAttributedString *result = [string mutableCopy];
  [result appendAttributedString:append];
  NSAttributedString *copy = [result copy];
  return copy;
}

//Instance method
- (NSAttributedString *)stringByAppendingString:(NSAttributedString *)append {
  NSMutableAttributedString *result = [self mutableCopy];
  [result appendAttributedString:append];
  NSAttributedString *copy = [result copy];
  return copy;
}
Camisado answered 12/11, 2015 at 18:49 Comment(0)
D
1

You can try SwiftyFormat It uses following syntax

let format = "#{{user}} mentioned you in a comment. #{{comment}}"
let message = NSAttributedString(format: format,
                                 attributes: commonAttributes,
                                 mapping: ["user": attributedName, "comment": attributedComment])
Decasyllable answered 14/1, 2017 at 0:24 Comment(1)
Can you please elaborate it more?How its will works?Iotacism
D
0
private var fillAttributes:[NSMutableAttributedString.Key : Any]? = nil

fontAttributes = [.foregroundColor : SKColor.red,
                  .strokeWidth : 0.0,
                  .font : CPFont(name: "Verdana-Bold",
                  .size : 50,]

fontAttributes.updateValue(SKColor.green, forKey: .foregroundColor)
Danyelledanyette answered 15/2, 2023 at 21:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.