Difference Between Nsrange and Nsmakerange

Difference between NSRange and NSMakeRange

The only difference between them is that

NSRange(location: 0, length: 5)

is an initializer for NSRange while

NSMakeRange(0, 5)

is a function which creates a new NSRange instance (by using the same initializer inside most likely) and actually is redundant in Swift. Swift has simply inherited it from Objective-C. I would stick to the former

NSRange from Swift Range?

Swift String ranges and NSString ranges are not "compatible".
For example, an emoji like counts as one Swift character, but as two NSString
characters (a so-called UTF-16 surrogate pair).

Therefore your suggested solution will produce unexpected results if the string
contains such characters. Example:

let text = "Long paragraph saying!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
let start = distance(text.startIndex, substringRange.startIndex)
let length = distance(substringRange.startIndex, substringRange.endIndex)
let range = NSMakeRange(start, length)

if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
}
})
println(attributedString)

Output:


Long paragra{
}ph say{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}ing!{
}

As you see, "ph say" has been marked with the attribute, not "saying".

Since NS(Mutable)AttributedString ultimately requires an NSString and an NSRange, it is actually
better to convert the given string to NSString first. Then the substringRange
is an NSRange and you don't have to convert the ranges anymore:

let text = "Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: nsText)

nsText.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
}
})
println(attributedString)

Output:


Long paragraph {
}saying{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}!{
}

Update for Swift 2:

let text = "Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstringsInRange(textRange, options: .ByWords, usingBlock: {
(substring, substringRange, _, _) in

if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
}
})
print(attributedString)

Update for Swift 3:

let text = "Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstrings(in: textRange, options: .byWords, using: {
(substring, substringRange, _, _) in

if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.red, range: substringRange)
}
})
print(attributedString)

Update for Swift 4:

As of Swift 4 (Xcode 9), the Swift standard library
provides method to convert between Range<String.Index> and NSRange.
Converting to NSString is no longer necessary:

let text = "Long paragraph saying!"
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstrings(in: text.startIndex..<text.endIndex, options: .byWords) {
(substring, substringRange, _, _) in
if substring == "saying" {
attributedString.addAttribute(.foregroundColor, value: NSColor.red,
range: NSRange(substringRange, in: text))
}
}
print(attributedString)

Here substringRange is a Range<String.Index>, and that is converted to the
corresponding NSRange with

NSRange(substringRange, in: text)

NSRange to Range String.Index

The NSString version (as opposed to Swift String) of replacingCharacters(in: NSRange, with: NSString) accepts an NSRange, so one simple solution is to convert String to NSString first. The delegate and replacement method names are slightly different in Swift 3 and 2, so depending on which Swift you're using:

Swift 3.0

func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {

let nsString = textField.text as NSString?
let newString = nsString?.replacingCharacters(in: range, with: string)
}

Swift 2.x

func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String) -> Bool {

let nsString = textField.text as NSString?
let newString = nsString?.stringByReplacingCharactersInRange(range, withString: string)
}

NSRange from first occurrence until end of string

Cast your String as NSString.

You will be able to use Foundation's .rangeOfString instead of Swift's .rangeOfString.

The Foundation's one will return an NSRange.

Be careful though, it doesn't work the same as Swift's method with Unicode, and NSRange and Range are not compatible (although there's ways to convert them).

lint Legacy Constructor Violation: (legacy_constructor) NSMakeRange in Swift?

The modern Swift constructor for this is NSRange(location:length:).

NSRange(location: 0, length: textfield.text!.characters.count)

Objective C: NSRange or similar with float?

Although you could reuse one of several "pair objects" from the graphics library (you can pick from a CGPoint or CGSize, or their NS... equivalents) the structs behind these objects are so simple that it would be better to create your own:

typedef struct FPRange {
float location;
float length;
} FPRange;

FPRange FPRangeMake(float _location, float _length) {
FPRange res;
res.location = _location;
res.length = _length;
return res;
}

Shortcut to generate an NSRange for entire length of NSString?

Function? Category method?

- (NSRange)fullRange
{
return (NSRange){0, [self length]};
}

[myString replaceOccurrencesOfString:@"replace_me"
withString:replacementString
options:NSCaseInsensitiveSearch
range:[myString fullRange]];

Why does my playground crash on NSRange()

You should be passing a UIColor for the foreground color instead of a CGColor.

UILabel seems to use the foreground color attribute slightly differently when drawing the string based on whether the attribute covers the entire range of the string or just a subrange.

The version it uses when the attribute covers the entire string only works with UIColor but the version it uses when the attribute only covers a substring seems to also work with CGColor (though this behavior isn't documented so it shouldn't be relied on) which explains why adding the -1 to the range avoids the exception.



Related Topics



Leave a reply



Submit