स्विफ्ट रेंज से NSRange?


175

समस्या: NSAttributedString एक NSRange लेता है जब मैं एक स्विफ्ट स्ट्रिंग का उपयोग करता हूं जो रेंज का उपयोग करता है

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

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

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

निम्नलिखित त्रुटि उत्पन्न करता है:

त्रुटि: 'श्रेणी' 'NSRange' के लिए परिवर्तनीय नहीं है। StringString.addAttribute (NSForegroundColorAttributeName, मूल्य: NSColor.redColor (), रेंज: substringRange)



2
@ सुहैब जो दूसरे रास्ते से जा रहा है।
जियोऑफ

जवाबों:


262

स्विफ्ट Stringपर्वतमाला और NSStringपर्वतमाला "संगत" नहीं हैं। उदाहरण के लिए, एक इमोजी जैसे character एक स्विफ्ट चरित्र के रूप में गिना जाता है, लेकिन दो NSString पात्रों के रूप में (तथाकथित यूटीएफ -16 सरोगेट जोड़ी)।

इसलिए आपके सुझाए गए समाधान अप्रत्याशित परिणाम उत्पन्न करेंगे यदि स्ट्रिंग में ऐसे वर्ण हैं। उदाहरण:

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)

आउटपुट:

😄😄😄लंग परागरा {
} ph कहते हैं {
    NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} आईएनजी! {
}

जैसा कि आप देखते हैं, "ph say" को विशेषता के साथ चिह्नित किया गया है, न कि "कह"।

के बाद से NS(Mutable)AttributedStringअंत में एक की आवश्यकता है NSStringऔर एक NSRange, यह वास्तव में करने के लिए दिया स्ट्रिंग परिवर्तित करने के लिए बेहतर है NSStringपहले। फिर substringRange एक है NSRangeऔर आपको अब और रेंज नहीं बदलनी हैं:

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)

आउटपुट:

😄😄😄लॉन्ग पैरा {
}कह रही है{
    NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}

स्विफ्ट 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)

स्विफ्ट 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)

स्विफ्ट 4 के लिए अपडेट:

स्विफ्ट 4 (एक्सकोड 9) के रूप में, स्विफ्ट मानक पुस्तकालय के बीच Range<String.Index>और में बदलने के लिए विधि प्रदान करता है NSRange। परिवर्तित करना NSStringअब आवश्यक नहीं है:

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)

यहाँ substringRangeएक है Range<String.Index>, और उस के NSRangeसाथ इसी में परिवर्तित किया गया है

NSRange(substringRange, in: text)

74
OSX पर इमोजी कैरेक्टर टाइप करने के इच्छुक किसी व्यक्ति के लिए - कंट्रोल-कमांड-स्पेस बार एक कैरेक्टर पिकर लाता है
Jay

2
यदि मैं एक से अधिक शब्दों का मिलान कर रहा हूं तो यह काम नहीं करता है, और मुझे यकीन नहीं है कि मैच के लिए पूरी स्ट्रिंग क्या है। मान लीजिए कि मुझे एक एपीआई से एक स्ट्रिंग वापस मिल रही है और इसे दूसरे स्ट्रिंग के भीतर उपयोग कर रहा है, और मैं चाहता हूं कि एपीआई से स्ट्रिंग को रेखांकित किया जाए, मैं यह गारंटी नहीं दे सकता कि सबस्ट्रिंग एपीआई और दूसरे से दोनों स्ट्रिंग में नहीं होंगे। स्ट्रिंग! कोई विचार?
सिमेन्टोम्पर

NSMakeRange परिवर्तित str.substringWithRange (रेंज <String.Index> (प्रारंभ: str.startIndex, अंत: str.endIndex)) // "हैलो, खेल का मैदान" यह परिवर्तन
HariKrishnan.P

(या) स्ट्रिंग कास्टिंग --- = सबस्ट्रिंग (NSString के रूप में स्ट्रिंग) .substringWithRange (NSMakeRange (शुरू, लंबाई)) जाने
HariKrishnan.P

2
आप उल्लेख करते हैं Range<String.Index>और NSStringसंगत नहीं हैं। क्या उनके समकक्ष भी असंगत हैं? यानी हैं NSRangeऔर Stringअसंगत हैं? क्योंकि Apple के एपीआई में से एक विशेष रूप से दो को जोड़ता है: मैच (में: विकल्प: रेंज :)
Senseful

56

आपके द्वारा वर्णित मामलों की तरह, मुझे यह काम करने के लिए मिला। यह अपेक्षाकृत छोटा और मीठा है:

 let attributedString = NSMutableAttributedString(string: "follow the yellow brick road") //can essentially come from a textField.text as well (will need to unwrap though)
 let text = "follow the yellow brick road"
 let str = NSString(string: text) 
 let theRange = str.rangeOfString("yellow")
 attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: theRange)

11
आरोपित
String.addAttribute

7
@Paludis, आप सही हैं लेकिन यह समाधान स्विफ्ट श्रेणी का उपयोग करने का प्रयास नहीं कर रहा है। यह एक का उपयोग कर रहा है NSRangestrएक है NSStringऔर इसलिए str.RangeOfString()एक रिटर्न NSRange
tjpaul 21

3
आप पंक्ति 2 में 2 और 3 को प्रतिस्थापित करके डुप्लिकेट स्ट्रिंग को 2 में भी निकाल सकते हैं:let str = attributedString.string as NSString
जेसन मूर

2
यह एक स्थानीयकरण दुःस्वप्न है।
सुल्तान

29

जवाब ठीक हैं, लेकिन स्विफ्ट 4 के साथ आप अपने कोड को थोड़ा सरल कर सकते हैं:

let text = "Test string"
let substring = "string"

let substringRange = text.range(of: substring)!
let nsRange = NSRange(substringRange, in: text)

सतर्क रहें, क्योंकि rangeफ़ंक्शन के परिणाम को अलिखित करना होगा।


10

संभावित समाधान

स्विफ्ट दूरी () प्रदान करता है जो एनएसआरएंगे बनाने के लिए शुरू और समाप्त होने के बीच की दूरी को मापता है:

let text = "Long paragraph saying something goes here!"
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)

//    println("word: \(substring) - \(d1) to \(d2)")

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

2
नोट: यह स्ट्रिंग में इमोजी जैसे पात्रों का उपयोग करके टूट सकता है - मार्टिन की प्रतिक्रिया देखें।
जेई

7

मेरे लिए यह पूरी तरह से काम करता है:

let font = UIFont.systemFont(ofSize: 12, weight: .medium)
let text = "text"
let attString = NSMutableAttributedString(string: "exemple text :)")

attString.addAttributes([.font: font], range:(attString.string as NSString).range(of: text))

label.attributedText = attString

5

स्विफ्ट 4:

ज़रूर, मुझे पता है कि स्विफ्ट 4 में पहले से ही NSRange का विस्तार है

public init<R, S>(_ region: R, in target: S) where R : RangeExpression,
    S : StringProtocol, 
    R.Bound == String.Index, S.Index == String.Index

मुझे पता है कि ज्यादातर मामलों में यह पर्याप्त है। इसका उपयोग देखें:

let string = "Many animals here: 🐶🦇🐱 !!!"

if let range = string.range(of: "🐶🦇🐱"){
     print((string as NSString).substring(with: NSRange(range, in: string))) //  "🐶🦇🐱"
 }

लेकिन स्विफ्ट के स्ट्रिंग उदाहरण के बिना रूपांतरण को सीधे रेंज <String.Index> से NSRange तक किया जा सकता है।

सामान्य इनिट उपयोग के बजाय आपको स्ट्रिंग के रूप में लक्ष्य पैरामीटर की आवश्यकता होती है और यदि आपके पास लक्ष्य स्ट्रिंग नहीं है तो आप सीधे रूपांतरण बना सकते हैं

extension NSRange {
    public init(_ range:Range<String.Index>) {
        self.init(location: range.lowerBound.encodedOffset,
              length: range.upperBound.encodedOffset -
                      range.lowerBound.encodedOffset) }
    }

या आप स्वयं रेंज के लिए विशेष एक्सटेंशन बना सकते हैं

extension Range where Bound == String.Index {
    var nsRange:NSRange {
    return NSRange(location: self.lowerBound.encodedOffset,
                     length: self.upperBound.encodedOffset -
                             self.lowerBound.encodedOffset)
    }
}

उपयोग:

let string = "Many animals here: 🐶🦇🐱 !!!"
if let range = string.range(of: "🐶🦇🐱"){
    print((string as NSString).substring(with: NSRange(range))) //  "🐶🦇🐱"
}

या

if let nsrange = string.range(of: "🐶🦇🐱")?.nsRange{
    print((string as NSString).substring(with: nsrange)) //  "🐶🦇🐱"
}

स्विफ्ट 5:

डिफ़ॉल्ट रूप से UTF-8 एन्कोडिंग में स्विफ्ट स्ट्रिंग्स के प्रवास के कारण, के उपयोग encodedOffsetको अपग्रेड माना जाता है और रेंज को स्वयं स्ट्रिंग के उदाहरण के बिना NSRange में परिवर्तित नहीं किया जा सकता है, क्योंकि ऑफ़सेट की गणना के लिए हमें उस स्रोत की आवश्यकता होती है जो है UTF-8 में एन्कोड किया गया और ऑफसेट की गणना करने से पहले इसे UTF-16 में बदल दिया जाना चाहिए। अब तक का सबसे अच्छा तरीका, सामान्य इनिट का उपयोग करना है ।


के उपयोग encodedOffsetहै हानिकारक माना जाएगा और पदावनत
मार्टिन आर

3

स्विफ्ट 4

मुझे लगता है, इसके दो तरीके हैं।

1. NSRange (रेंज, में:)

2. NSRange (स्थान :, लंबाई:)

नमूना कोड:

let attributedString = NSMutableAttributedString(string: "Sample Text 12345", attributes: [.font : UIFont.systemFont(ofSize: 15.0)])

// NSRange(range, in: )
if let range = attributedString.string.range(of: "Sample")  {
    attributedString.addAttribute(.foregroundColor, value: UIColor.orange, range: NSRange(range, in: attributedString.string))
}

// NSRange(location: , length: )
if let range = attributedString.string.range(of: "12345") {
    attributedString.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset))
}

स्क्रीन शॉट: यहां छवि विवरण दर्ज करें


के उपयोग encodedOffsetहै हानिकारक माना जाएगा और पदावनत
मार्टिन आर

1

स्विफ्ट 3 एक्सटेंशन वेरिएंट जो मौजूदा विशेषताओं को संरक्षित करता है।

extension UILabel {
  func setLineHeight(lineHeight: CGFloat) {
    guard self.text != nil && self.attributedText != nil else { return }
    var attributedString = NSMutableAttributedString()

    if let attributedText = self.attributedText {
      attributedString = NSMutableAttributedString(attributedString: attributedText)
    } else if let text = self.text {
      attributedString = NSMutableAttributedString(string: text)
    }

    let style = NSMutableParagraphStyle()
    style.lineSpacing = lineHeight
    style.alignment = self.textAlignment
    let str = NSString(string: attributedString.string)

    attributedString.addAttribute(NSParagraphStyleAttributeName,
                                  value: style,
                                  range: str.range(of: str as String))
    self.attributedText = attributedString
  }
}

0
func formatAttributedStringWithHighlights(text: String, highlightedSubString: String?, formattingAttributes: [String: AnyObject]) -> NSAttributedString {
    let mutableString = NSMutableAttributedString(string: text)

    let text = text as NSString         // convert to NSString be we need NSRange
    if let highlightedSubString = highlightedSubString {
        let highlightedSubStringRange = text.rangeOfString(highlightedSubString) // find first occurence
        if highlightedSubStringRange.length > 0 {       // check for not found
            mutableString.setAttributes(formattingAttributes, range: highlightedSubStringRange)
        }
    }

    return mutableString
}

0

मैं स्विफ्ट भाषा से प्यार करता हूं, लेकिन NSAttributedStringएक स्विफ्ट के साथ उपयोग करना जो Rangeसंगत नहीं NSRangeहै, ने मेरे सिर को बहुत लंबे समय तक चोट पहुंचाई है। तो उस कचरे के चारों ओर पाने के लिए मैंने NSMutableAttributedStringआपके रंग के साथ हाइलाइट किए गए शब्दों के साथ वापस जाने के लिए निम्नलिखित विधियों को तैयार किया ।

यह इमोजीस के लिए काम नहीं करता है। अगर आपको चाहिए तो संशोधित करें।

extension String {
    func getRanges(of string: String) -> [NSRange] {
        var ranges:[NSRange] = []
        if contains(string) {
            let words = self.components(separatedBy: " ")
            var position:Int = 0
            for word in words {
                if word.lowercased() == string.lowercased() {
                    let startIndex = position
                    let endIndex = word.characters.count
                    let range = NSMakeRange(startIndex, endIndex)
                    ranges.append(range)
                }
                position += (word.characters.count + 1) // +1 for space
            }
        }
        return ranges
    }
    func highlight(_ words: [String], this color: UIColor) -> NSMutableAttributedString {
        let attributedString = NSMutableAttributedString(string: self)
        for word in words {
            let ranges = getRanges(of: word)
            for range in ranges {
                attributedString.addAttributes([NSForegroundColorAttributeName: color], range: range)
            }
        }
        return attributedString
    }
}

उपयोग:

// The strings you're interested in
let string = "The dog ran after the cat"
let words = ["the", "ran"]

// Highlight words and get back attributed string
let attributedString = string.highlight(words, this: .yellow)

// Set attributed string
label.attributedText = attributedString

-3
let text:String = "Hello Friend"

let searchRange:NSRange = NSRange(location:0,length: text.characters.count)

let range:Range`<Int`> = Range`<Int`>.init(start: searchRange.location, end: searchRange.length)

6
कैसे अपने जवाब को थोड़ा समझाने के बारे में, और अधिमानतः कोड को ठीक से प्रारूपित करना?
सैमबी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.