Are there any analogues [NSString stringWithFormat:] for NSAttributedString

I usually create an application interface in an interface builder. Sometimes design requires the use of attributed strings (fonts, colors, etc.). It is easy to configure whether a string is static.
But if the string is dynamic (format with arguments), then there is no way to configure attributes in the interface builder. It requires to write a lot of code.
I am looking for some analogs of [NSString stringWithFormat:] for NSAttributedString . Therefore, I can set the string format and the necessary attributes in the interface builder, and then provide the necessary arguments in the code.

For instance:
Suppose I need a display string with this format: " % d + % d = % d " (all numbers are in bold). <w> I want to configure this format in the interface builder. In the code I want to provide the arguments: 1, 1, 2. The application should show " 1 + 1 = 2 ".

+10
source share
4 answers

I was looking for a good existing solution for this issue, but was not successful.
Therefore, I was able to implement this on my own.
That's why I myself answer the question about sharing knowledge with the community.

Decision

The NSAttributedString + VPAttributedFormat category provides methods for constructing an attribute string based on an attribute format and arguments that must satisfy this format.
The most suitable use case for this category is text controls with a variable text attribute configured in the interface builder.
You need to set the correct string format for the text attribute and configure the necessary attributes.
Then you need to pass the necessary arguments in the code using the methods of this category.

  • The syntax format is the same as in the [NSString stringWithFormat:] method;
  • Can be used in Objective-C and Swift code;
  • Requires iOS 6.0 and later;
  • Integrated with CocoaPods;
  • Complete with unit tests.

Using

1. Import a header or structure module

 // Objective C // By header #import <VPAttributedFormat/VPAttributedFormat.h> // By module @import VPAttributedFormat; 

 // Swift import VPAttributedFormat 

2. Set the correct format and attributes for text management in the interface builder
usage

3. Create an IBOutlet and bind it using text management

 // Objective C @property (nonatomic, weak) IBOutlet UILabel *textLabel; 

 // Swift @IBOutlet weak var textLabel: UILabel! 

4. Set the format with the necessary arguments

 // Objective C NSString *hot = @"Hot"; NSString *cold = @"Cold"; self.textLabel.attributedText = [NSAttributedString vp_attributedStringWithAttributedFormat:self.textLabel.attributedText, hot, cold]; 

 // Swift let hot = "Hot" let cold = "Cold" var arguments: [CVarArgType] = [hot, cold] textLabel.attributedText = withVaList(arguments) { pointer in NSAttributedString.vp_attributedStringWithAttributedFormat(textLabel.attributedText, arguments: pointer) } 

5. See Result
result

Examples

VPAttributedFormatExample is a sample project. It provides examples of Basic and Pro formats.
example

+4
source

Here is the category I wrote to add a method to NSAttributedString. You will need to pass NULL as the last argument to the function, however otherwise it will fail to va_list constraints for sizing. [attribitedString stringWithFormat: attrFormat, attrArg1, attrArg2, NULL];

 @implementation NSAttributedString(stringWithFormat) +(NSAttributedString*)stringWithFormat:(NSAttributedString*)format, ...{ va_list args; va_start(args, format); NSMutableAttributedString *mutableAttributedString = (NSMutableAttributedString*)[format mutableCopy]; NSString *mutableString = [mutableAttributedString string]; while (true) { NSAttributedString *arg = va_arg(args, NSAttributedString*); if (!arg) { break; } NSRange rangeOfStringToBeReplaced = [mutableString rangeOfString:@"%@"]; [mutableAttributedString replaceCharactersInRange:rangeOfStringToBeReplaced withAttributedString:arg]; } va_end(args); return mutableAttributedString; } @end 
+4
source

Here is a Swift 4 extension based on TheJeff's answer (fixed for multiple lookups). It is limited to replacing placeholders with NSAttributedString:

 public extension NSAttributedString { convenience init(format: NSAttributedString, args: NSAttributedString...) { let mutableNSAttributedString = NSMutableAttributedString(attributedString: format) var nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@") var param = 0 while nsRange.location != NSNotFound { guard args.count > 0, param < args.count else { fatalError("Not enough arguments provided for \(format)") } mutableNSAttributedString.replaceCharacters(in: nsRange, with: args[param]) param += 1 nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@") } self.init(attributedString: mutableNSAttributedString) } } 
+2
source

Compatible with Swift 4.2

 public extension NSAttributedString { convenience init(format: NSAttributedString, args: NSAttributedString...) { let mutableNSAttributedString = NSMutableAttributedString(attributedString: format) args.forEach { (attributedString) in let range = NSString(string: mutableNSAttributedString.string).range(of: "%@") mutableNSAttributedString.replaceCharacters(in: range, with: attributedString) } self.init(attributedString: mutableNSAttributedString) } } 

Using:

 let content = NSAttributedString(string: "The quick brown %@ jumps over the lazy %@") let fox = NSAttributedString(string: "fox", attributes: [.font: Fonts.CalibreReact.boldItalic]) let dog = NSAttributedString(string: "dog", attributes: [.font: Fonts.CalibreReact.lightItalic]) attributedLabel.attributedText = NSAttributedString(format: content, args: fox, dog) 

Result:

enter image description here

0
source

All Articles