web-dev-qa-db-fra.com

Souligner la partie d'une chaîne utilisant NSMutableAttributedString dans iOS 8 ne fonctionne pas

J'essaie de souligner une partie de chaîne, par exemple une chaîne ' string ' dans ' test string '. J'utilise NSMutableAttributedString et ma solution fonctionnait bien avec iOS7.

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]
        initWithString:@"test string"];
[attributedString addAttribute:NSUnderlineStyleAttributeName
                         value:@(NSUnderlineStyleSingle)
                         range:NSMakeRange(5, 6)];
myLabel.attributedText = attributedString;

Le problème est que ma solution ne fonctionne plus dans iOS8. Après avoir passé une heure à tester plusieurs variantes de NSMutableAttributedString, j'ai découvert que cette solution ne fonctionne que lorsque la plage commence par 0 (la longueur peut varier). Quelle est la raison de ceci? Comment puis-je contourner ce problème?

56
KlimczakM

Mise à jour: En examinant cette question: Afficher NSMutableAttributedString sur iOS 8 J'ai enfin trouvé la solution!

Vous devez ajouter NSUnderlineStyleNone au début de la chaîne.

Swift 4.2 (none a été supprimé): 

let attributedString = NSMutableAttributedString()
attributedString.append(NSAttributedString(string: "test ",
                                           attributes: [.underlineStyle: 0]))
attributedString.append(NSAttributedString(string: "s",
                                           attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue]))
attributedString.append(NSAttributedString(string: "tring",
                                           attributes: [.underlineStyle: 0]))

Objectif c:

 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
 [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"test "
                                                                          attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleNone)}]];
 [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"s"
                                                                         attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
                                                                                      NSBackgroundColorAttributeName: [UIColor clearColor]}]];
 [attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"tring"]];

Un autre avantage d'une telle approche est l'absence de toute plage. Très agréable pour les chaînes localisées.

On dirait qu'il s'agit d'un bug Apple :(

119
kelin

J'ai constaté que si vous appliquez UnderlineStyleNone à la chaîne entière, vous pouvez ensuite appliquer de manière sélective le soulignement à une partie commençant au milieu:

func underlinedString(string: NSString, term: NSString) -> NSAttributedString {
    let output = NSMutableAttributedString(string: string)
    let underlineRange = string.rangeOfString(term)
    output.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleNone.rawValue, range: NSMakeRange(0, string.length))
    output.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: underlineRange)

    return output
}
17
exidy
NSMutableAttributedString *signUpString = [[NSMutableAttributedString alloc] initWithString:@"Not a member yet?Sign Up now"];

[signUpString appendAttributedString:[[NSAttributedString alloc] initWithString:@" "
                                                                         attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleNone)}]];

[signUpString addAttributes: @{NSForegroundColorAttributeName:UIColorFromRGB(0x43484B),NSUnderlineStyleAttributeName:[NSNumber numberWithInteger:NSUnderlineStyleSingle]} range:NSMakeRange(17,11)];

signUpLbl.attributedText = [signUpString copy];

Ça a fonctionné pour moi

6
Tanuj Jagoori

version de Swift 5 de la réponse de @ Joss avec peu de modifications, en ajoutant un NSMutableAttributedString renvoyé, car je ne pourrais pas utiliser la solution originale sans ce dernier.

extension NSMutableAttributedString {

func underline(term: String) -> NSMutableAttributedString {
    guard let underlineRange = string.range(of: term) else {
        return NSMutableAttributedString()
    }
    let startPosition = string.distance(from: term.startIndex, to: underlineRange.lowerBound)
    let nsrange = NSRange(location: startPosition, length: term.count)
    addAttribute(
        .underlineStyle,
        value: NSUnderlineStyle.single.rawValue,
        range: nsrange)
     return self
}

}

Usage:

 let myUnderLinedText = "Hello World"
 let underLinedMutableString =  NSMutableAttributedString(string: myUnderLinedText, attributes: titleAttributes).underline(term: myUnderLinedText)
0
Mussa Charles

J'ai utilisé l'extension suivante (en utilisant la fonction exidy) dans la plaine de jeux/le simulateur et cela a bien fonctionné, vous pouvez modifier/ajouter des attributs en fonction de vos besoins.

 extension NSMutableAttributedString
{


    func changeWordsColour(terms:[NSString])
{
    let string = self.string as NSString
    self.addAttribute(NSForegroundColorAttributeName, value: UIColor.brownColor(), range: NSMakeRange(0, self.length))
    for term in terms
    {
        let underlineRange = string.rangeOfString(term as String)
        self.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: underlineRange)

    }
}
}

 let myStr = NSMutableAttributedString(string: "Change Words Colour")
 myStr.changeWordsColour(["change","Colour"])
0
Atka

Nous sommes en septembre 2018, donc cette réponse ne concerne pas iOS8, mais concerne toujours le soulignement d'une partie d'une chaîne.

Voici une extension Swift 4 qui souligne un terme donné dans un myAttributedString déjà composé

extension NSMutableAttributedString {

    func underline(term: String) {

        guard let underlineRange = string.range(of: term) else {

            return
        }

        let startPosition = string.distance(from: term.startIndex, to: underlineRange.lowerBound)
        let nsrange = NSRange(location: startPosition, length: term.count)

        addAttribute(
            .underlineStyle,
            value: NSUnderlineStyle.styleSingle.rawValue,
            range: nsrange)
    }
}

Usage: myAttributedString.underline(term: "some term")

0
Joss