186

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?

Imanou Petit
  • 89,880
  • 29
  • 256
  • 218

9 Answers9

222

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.

Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
106

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")
David Lawson
  • 7,802
  • 4
  • 31
  • 37
algal
  • 27,584
  • 13
  • 78
  • 80
  • but result is mutable! it doesn't make sense for types! – gaussblurinc Nov 12 '15 at 13:15
  • 5
    the mutable class is a subtype of the immutable class. – algal Nov 14 '15 at 08:13
  • so I can cast result to mutable counterpart and get no error? – gaussblurinc Nov 14 '15 at 11:06
  • 4
    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. – algal Nov 15 '15 at 18:25
  • 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 one – gaussblurinc Nov 15 '15 at 18:32
  • 6
    Yes, you should do a defensive copy if you want to be defensive. (Not sarcasm.) – algal Nov 15 '15 at 18:42
  • 1
    If you really want to return NSAttributedString, then perhaps this would work: `return NSAttributedString(attributedString: result)` – Alex Sep 16 '16 at 10:29
  • 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... – n13 Nov 12 '16 at 06:23
  • 2
    @n13 I would create a folder called `Helpers` or `Extensions` and put this function in a file named `NSAttributedString+Concatenate.swift`. – David Lawson Nov 20 '16 at 08:55
42

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
    ]
Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66
Josh O'Connor
  • 4,694
  • 7
  • 54
  • 98
28

Try this:

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

Where astring1 and astring2 are NSAttributedStrings.

Linuxios
  • 34,849
  • 13
  • 91
  • 116
  • 13
    Or `[[aString1 mutableCopy] appendAttributedString: aString2]`. – JWWalker Aug 29 '13 at 18:26
  • @JWWalker your 'oneliner' is corrupted. you can't get this "concatenation" result because appendAttributedString doesn't return string. Same story with dictionaries – gaussblurinc Nov 12 '15 at 13:14
  • @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];`. – JWWalker Nov 12 '15 at 15:55
  • @gaussblurinc, JWalker: Corrected the answer. – Linuxios Nov 12 '15 at 16:44
  • @Linuxios, also, you return `result` as `NSMutableAttributedString`. it is not what author want to see. `stringByAppendingString` - this method will be good – gaussblurinc Nov 12 '15 at 16:57
  • @gaussblurinc: The original post isn't clear enough for us to know that, and I don't really see a problem with this method. – Linuxios Nov 12 '15 at 16:59
  • @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 `NSAttributedString` – gaussblurinc Nov 12 '15 at 17:05
  • @gaussblurinc: Unless I'm misunderstanding inheritance in Obj-c, a mutable attributed string can be used anywhere a normal one can. – Linuxios Nov 12 '15 at 17:09
  • @Linuxios 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. – gaussblurinc Nov 12 '15 at 18:22
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94962/discussion-between-linuxios-and-gaussblurinc). – Linuxios Nov 12 '15 at 18:23
16

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
    }
}
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
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.

DiscDev
  • 38,652
  • 20
  • 117
  • 133
fatuhoku
  • 4,815
  • 3
  • 30
  • 70
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;
}
gaussblurinc
  • 3,642
  • 9
  • 35
  • 64
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])
Igor Palaguta
  • 3,579
  • 2
  • 20
  • 32
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)
Mateus
  • 357
  • 1
  • 4
  • 14