Find attributes from attributed string that user typed
You can enumerate the Attributes with this code (whole range).
[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length]) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:
^(NSDictionary *attributes, NSRange range, BOOL *stop) {
//Do something here
}
}];
For specific range, your have to change the code NSMakeRange(0, [attrStr lenght])
by the range you need.
After your edit, your specific need, this code should do the work:
[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:
^(NSDictionary *attributes, NSRange range, BOOL *stop)
{
NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
UIFont *currentFont = [mutableAttributes objectForKey: NSFontAttributeName];
UIFont *newFont;
if ([[currentFont fontName] isEqualToString:@"Calibri-BoldItalic"])
newFont = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:currentFont.pointSize];
else if ([[currentFont fontName] isEqualToString:@"Calibri-Bold"])
newFont = [UIFont fontWithName:@"HelveticaNeue-Bold" size:currentFont.pointSize];
else if ([[currentFont fontName] isEqualToString:@"Calibri-Italic"])
newFont = [UIFont fontWithName:@"HelveticaNeue-Italic" size:currentFont.pointSize];
[mutableAttributes setObject:newFont forKey:@"NSFont"];
[attrStr setAttributes:mutableAttributes range:range];
}];
From your comments, you may see more complicated things, so I will give you some ideas/tips to do/check. You'll just have to complete my previous code to manage all cases.
• Case: There is no font.
You just have to check if currentFont
is nil. If yes, you set what you want. Since there is no attribute for italic/bold (since they are "included" in the font), I suggest you set for newFont
: HelveticaNeue.
• Case: It's another font than Calibri. There it's quite more complicated.
I've worked with that, and here what I got:
Font name (which is the postscript name
) is made like this for HelveticaNeue (as given example):
HelveticaNeue
HelveticaNeue-Light
HelveticaNeue-UltraLight
HelveticaNeue-Medium
HelveticaNeue-LightItalic
HelveticaNeue-Italic
HelveticaNeue-UltraLightItalic
HelveticaNeue-Bold
HelveticaNeue-BoldItalic
HelveticaNeue-CondensedBold
HelveticaNeue-CondensedBlack
So the first thing you could think of: I just have to check the suffix of the name (with hasSuffix:
). But that may go wrong.
Indeed, some fonts have a foundry suffix (the ones I found are LT, MT, and EF).
Well, for example:
Times New Roman "real name" is TimesNewRomanPSMT.
And its italic version is TimesNewRomanPS-ItalicMT
That's why we have to check if it has a foundry suffix because TimesNewRomanPSMT-Italic won't work (since it doesn't exist, if you try to set a font with this name, it will be nil).
So, as far as I know, and as far I may have some experience and the case I've seen, what you could do:
Check if the [currentFont name]
contains either: BoldItalic
, Italic
or Bold
, using a NSRegularExpression
or a rangeOfString:
. Note that I'd suggest to check before for BoldItalic
since an Italic
may "hides" a Bold
just before it, and Bold
may "hides" an Italic
just after it.
In order to do so, I'd suggest that you create a method like this (or of this style):
-(UIFont)getCorrespondingHelveticaNeueFontMatchingCharacteriticsOf:(UIFont *)font
{
UIFont *helveticaNueue;
if (!font) //Checking if there is a font)
{
helveticaNueue = [UIFont fontWithName@"HelveticaNueue" size:defaultSizeYouSet];
}
else
{
//Check if it's BoldItalic/Italic/Bold
}
return helveticaNueue;
}
checking all the case I mentioned before.
So, from the previous code, you'll just have to replace the if
tests that were working specificly for Calibri with:
UIFont *newFont = [self getCorrespondingHelveticaNeueFontMatchingCharacteriticsOf:currentFont];
Now a third part you may want to do (and add them to your test):
Is the Black
case of a font want to be managed as Bold
? Well, Helvetica Neue
has this "property", but you may want to use directly the black
property for Helvetica Neue
, and may want to check if currentFont
has it (and again, another case).
EDIT:
You can use since iOS7 we can use UIFontDescriptor
. Here is what you could do:
-(UIFont *)customFontMatchingFont:(UIFont *)otherFont
{
NSString *customFontName = @"Helvetica"; //The custom font
UIFont *tempFont = [UIFont fontWithName:customFontName size:[otherFont pointSize]];
return [UIFont fontWithDescriptor:[[tempFont fontDescriptor] fontDescriptorWithSymbolicTraits:[[otherFont fontDescriptor] symbolicTraits]] size:[otherFont pointSize]];
}
Note: I didn't check all the cases (what if your custom family doesn't have a matching font, what does it return? etc.), but that could be a nice add-on if you don't do too complicated things.
Is it possible to get a listing of attributes and ranges for an NSMutableAttributedString?
Apple expects you to use enumerateAttributesInRange:options:usingBlock:
. The block you supply will receive ranges and the attributes applicable for that range.
I've used that in my code to create invisible buttons that are placed behind text so that it acts as a hyperlink.
You could also use enumerateAttribute:inRange:options:usingBlock:
if there's only one you're interested in, but no halfway house is provided where you might be interested in, say, two attributes but not every attribute.
Get all attributes of a NSMutableAttributedString into a dictionary
Convert to html
var s: NSAttributedString? = "Pizza stirng"
var documentAttributes: [AnyHashable: Any] = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
var htmlData: Data? = try? s?.data(from: NSRange(location: 0, length: s?.length), documentAttributes: documentAttributes as? [String : Any] ?? [String : Any]())
var htmlString = String(data: htmlData, encoding: String.Encoding.utf8)
and try this to get back NSAttributedString
var atributedStr = try? NSAttributedString(data: htmlString.data(using: String.Encoding.utf8), options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: (String.Encoding.utf8)], documentAttributes: nil)
Swift 3: Getting attributes at substring in NSAttributedString
Here's how you can do it without hardcoding. This is Swift 3 playground code based on your sample:
import UIKit
import PlaygroundSupport
let linkStr = "Click <a href='http://google.com'>here</a> for good times."
let attributedText = try! NSAttributedString(
data: linkStr.data(using: String.Encoding.unicode, allowLossyConversion: true)!,
options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
attributedText.enumerateAttribute(NSAttributedString.Key.link, in: NSMakeRange(0, attributedText.length), options: [.longestEffectiveRangeNotRequired]) { value, range, isStop in
if let value = value {
print("\(value) found at \(range.location)")
}
}
The print
statement outputs:
http://google.com/ found at 6
p.s. 'NSAttributedString.Key.link' is instead of 'NSLinkAttributeName' because of the renaming.
Swift 4 attributedString get typing attributes
You can map the [String: Any]
dictionary to a[NSAttributedStringKey: Any]
dictionary with
let typingAttributes = Dictionary(uniqueKeysWithValues: self.textView.typingAttributes.map {
key, value in (NSAttributedStringKey(key), value)
})
let text = NSAttributedString(string: "test123", attributes: typingAttributes)
Here is a possible extension method for that purpose, it is
restricted to dictionaries with string keys:
extension Dictionary where Key == String {
func toAttributedStringKeys() -> [NSAttributedStringKey: Value] {
return Dictionary<NSAttributedStringKey, Value>(uniqueKeysWithValues: map {
key, value in (NSAttributedStringKey(key), value)
})
}
}
How to search word in a attributed text in swift 3?
Issue:
let attributedString = NSMutableAttributedString(string: targetString)
You are creating a NSAttributedString
with your HTML string without parsing. So you see the HTML tags.
You have already your own method that parse a HTML string into a NSAttributedString
, use it (and keep in mind we need a mutable one):
let attributedString = NSMutableAttributedString(attributedString: targetString.htmlAttributedString(fontSize: 14.0))
Now, the NSAttributedString
conversion removed the HTML tags (and interprets them if possible, because NSAttributedString
doesn't interprets ALL HTML tags, only a few ones). So the length, the ranges are all different.
So you can't do this anymore:
let range = NSRange(location: 0, length: targetString.utf16.count)
You need to update it to:
let range = NSRange(location: 0, length: attributedString.string.utf16.count)
Same here:
for match in regex.matches(in: targetString, options: .withTransparentBounds, range: range) {
To be updated to:
for match in regex.matches(in: attributedString.string, options: .withTransparentBounds, range: range) {
Get the number of strings that have a url attribute in an attributed string
You’re close. For this you can use the enumerateAttribute(_:in:options:using:)
method. You pass in the attribute you are interested in (in your case .link
) and it calls the closure with each subrange of the string with the range and the value of the attribute. This includes subranges that don’t have that attribute. In this case nil
is passed for the value. So to count the links in your string you count the non-nil attributes:
func linksIn(_ attributedString: NSAttributedString) -> Int {
var count = 0
attributedString.enumerateAttribute(.link, in: NSRange(location: 0, length: attributedString.length), options: []) { attribute, _, _ in
if attribute != nil {
count += 1
}
}
return count
}
Related Topics
How to Call Method from Viewcontroller in Gamescene
Undefined Symbols for Architecture I386: "_Objc_Class_$_Zipexception", Referenced From: Error
How to Determine the True Data Type of an Nsnumber
How to Programmatically Get iOS Status Bar Height
iOS Uialertview Button to Go to Setting App
How to Hide "-" (Delete) Button While Editing Uitableview
Drawviewhierarchyinrect:Afterscreenupdates: Delays Other Animations
How to Add a Button with Click Event on Uitableviewcell in Swift
Get Date and Time from Apple Server
Xcode Cannot Run on the Selected Destination
Storyboard Segues Causing Memory Leaks
Confused About Orthographic Projection of Camera in Scenekit
How to Implement Login/Logout Navigation Using Userdefaults in Swift
"Too Many Symbol Files" After Successfully Submitting My Apps
How to Get Hour and Minutes from Nsdate
How to Find Out Distance Between Coordinates
Styling the Cancel Button in a Uisearchbar
How to Create Static Library and Can Add Just .A File on Any Project in iOS