Comment changer les couleurs d'un segment dans un UISegmentedControl sous iOS 13?

109

A UISegmentedControla une nouvelle apparence dans iOS 13 et le code existant pour modifier les couleurs du contrôle segmenté ne fonctionne plus comme ils le faisaient.

Avant iOS 13, vous pouviez définir le tintColoret qui serait utilisé pour la bordure autour du contrôle segmenté, les lignes entre les segments et la couleur d'arrière-plan du segment sélectionné. Ensuite, vous pouvez changer la couleur des titres de chaque segment en utilisant l'attribut de couleur de premier plan avec titleTextAttributes.

Sous iOS 13, le tintColorne fait rien. Vous pouvez définir le contrôle segmenté backgroundColorpour changer la couleur globale du contrôle segmenté. Mais je ne trouve aucun moyen de modifier la couleur utilisée comme arrière-plan du segment sélectionné. La définition des attributs de texte fonctionne toujours. J'ai même essayé de définir la couleur d'arrière-plan du titre, mais cela n'affecte que l'arrière-plan du titre, pas le reste de la couleur d'arrière-plan du segment sélectionné.

En bref, comment modifier la couleur d'arrière-plan du segment actuellement sélectionné d'un UISegmentedControldans iOS 13? Existe-t-il une solution appropriée, utilisant des API publiques, qui ne nécessite pas de creuser dans la structure de sous-vue privée?

Il n'y a pas de nouvelles propriétés dans iOS 13 pour UISegmentedControlou UIControlet aucune des modifications apportées UIViewn'est pertinente.

rmaddy
la source

Réponses:

134

Comme d'iOS 13B3, il est maintenant selectedSegmentTintColorsur UISegmentedControl.

Pour modifier la couleur globale du contrôle segmenté, utilisez son backgroundColor.

Pour changer la couleur du segment sélectionné, utilisez selectedSegmentTintColor.

Pour changer la couleur / police des titres de segment non sélectionnés, utilisez setTitleTextAttributesavec un état de .normal/ UIControlStateNormal.

Pour changer la couleur / la police des titres de segment sélectionnés, utilisez setTitleTextAttributesavec un état de.selected /UIControlStateSelected .

Si vous créez un contrôle segmenté avec des images, si les images sont créées en tant qu'images de modèle, le contrôle segmenté tintColorsera utilisé pour colorer les images. Mais cela pose un problème. Si vous définissez la tintColormême couleur que, selectedSegmentTintColorl'image ne sera pas visible dans le segment sélectionné. Si vous définissez la tintColormême couleur que backgroundColor, les images des segments non sélectionnés ne seront pas visibles. Cela signifie que votre contrôle segmenté avec des images doit utiliser 3 couleurs différentes pour que tout soit visible. Ou vous pouvez utiliser des images non modèles et ne pas définir le tintColor.

Sous iOS 12 ou version antérieure, définissez simplement les contrôles segmentés tintColorou comptez sur la couleur de teinte globale de l'application.

rmaddy
la source
Comment définir le contrôleur de segment sans frontières? Je ne vois aucun paramètre pour cela dans iOS 13. Auparavant, le réglage de la teinte était suffisant pour obtenir un contrôle de segment sans bordure.
Deepak Sharma
Veuillez ajouter la couleur de la bordure, etc. afin que tous puissent trouver tous les problèmes liés à la couleur du segment sont résolus ici. que dire? :)
Yogesh Patel
1
@YogeshPatel Et la couleur de la bordure? Il n'y a pas de couleur de bordure dans iOS 13 et dans iOS 12, elle est définie avec tintColorce qui est déjà couvert dans la réponse.
rmaddy
@rmaddy J'ai défini ceci [segmentedControl.layer setBorderColor: [[UIColor whiteColor] CGColor]]; [segmentedControl.layer setBorderWidth: 0,5]; cela me donne la couleur des bordures et des bordures sous iOS 13.
Yogesh Patel
1
Oh, cette frontière. Cela s'applique à n'importe quelle vue, pas seulement à un contrôle segmenté. Cela dépasse la portée de la question initiale et de cette réponse. Votre commentaire suffit.
rmaddy
47

À partir de Xcode 11 beta 3

Il y a maintenant la selectedSegmentTintColorpropriété sur UISegmentedControl.

Voir la réponse de rmaddy


Pour récupérer l'apparence d'iOS 12

Je n'ai pas pu teinter la couleur du segment sélectionné, j'espère qu'il sera corrigé dans une prochaine version bêta.

La définition de l'image d'arrière-plan de l'état sélectionné ne fonctionne pas sans la définition de l'image d'arrière-plan de l'état normal (qui supprime tout le style iOS 13)

Mais j'ai pu le ramener à l'apparence d'iOS 12 (ou assez près, je n'ai pas pu ramener le rayon de coin à sa plus petite taille).

Ce n'est pas idéal, mais un contrôle segmenté blanc brillant semble un peu déplacé dans notre application.

(Je n'avais pas réalisé qu'il UIImage(color:)s'agissait d'une méthode d'extension dans notre base de code. Mais le code pour l'implémenter se trouve sur le Web)

extension UISegmentedControl {
    /// Tint color doesn't have any effect on iOS 13.
    func ensureiOS12Style() {
        if #available(iOS 13, *) {
            let tintColorImage = UIImage(color: tintColor)
            // Must set the background image for normal to something (even clear) else the rest won't work
            setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
            setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
            setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
            setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
            layer.borderWidth = 1
            layer.borderColor = tintColor.cgColor
        }
    }
}

Image montrant l'effet du code ci-dessus

Jonathan.
la source
Cela pourrait être une bonne solution de contournement. Je n'ai pas encore eu l'occasion d'essayer cela, mais cela nécessite-t-il également un appel setTitleTextAttributespour que le titre du segment sélectionné ait une couleur blanche?
rmaddy
Hmm, on dirait que ça devrait mais apparemment pas. Je n'ai pas accès à ce atm d'utilisation du code, mais l'image de gauche est créée avec ce code.
Jonathan.
8
stackoverflow.com/a/33675160/5790492 pour l'extension UIImage (couleur :).
Nik Kov
1
Ce serait bien si vous ajoutiez l'extension UIImage, de cette façon, votre réponse n'est pas complète iho
FredFlinstone
1
@VityaShurapov le définit pour quand il est à la fois mis en surbrillance et sélectionné, ce n'est pas un tableau d'états passés mais plutôt un ensemble d'options, ce qui signifie que les valeurs sont combinées pour créer un nouvel état.
Jonathan.
36

Contrôle de segment IOS 13 et Swift 5.0 (Xcode 11.0) 100% fonctionnel

entrez la description de l'image ici

entrez la description de l'image ici

 if #available(iOS 13.0, *) {
      yoursegmentedControl.backgroundColor = UIColor.black
      yoursegmentedControl.layer.borderColor = UIColor.white.cgColor
      yoursegmentedControl.selectedSegmentTintColor = UIColor.white
      yoursegmentedControl.layer.borderWidth = 1

      let titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]    
      yoursegmentedControl.setTitleTextAttributes(titleTextAttributes, for:.normal)

      let titleTextAttributes1 = [NSAttributedString.Key.foregroundColor: UIColor.black]
      yoursegmentedControl.setTitleTextAttributes(titleTextAttributes1, for:.selected)
  } else {
              // Fallback on earlier versions
}
Maulik Patel
la source
7
avez-vous essayé de définir l'arrière-plan en blanc? pour moi il vient s grisâtre
Ronit le
@ItanHant quel xcode et quelle langue rapide utilisez-vous?
Maulik Patel
@Ronit bien sûr !! Je vais essayer mais plz me dire quel type d'affichage de sortie maintenant
Maulik Patel
swift 5, xcode 11.3! ça montre ce qu'il veut! pas celui que je veux :)
Itan Hant
15

J'ai essayé la solution de contournement et cela fonctionne très bien pour moi. Voici la version Objective-C:

@interface UISegmentedControl (Common)
- (void)ensureiOS12Style;
@end
@implementation UISegmentedControl (Common)
- (void)ensureiOS12Style {
    // UISegmentedControl has changed in iOS 13 and setting the tint
    // color now has no effect.
    if (@available(iOS 13, *)) {
        UIColor *tintColor = [self tintColor];
        UIImage *tintColorImage = [self imageWithColor:tintColor];
        // Must set the background image for normal to something (even clear) else the rest won't work
        [self setBackgroundImage:[self imageWithColor:self.backgroundColor ? self.backgroundColor : [UIColor clearColor]] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        [self setBackgroundImage:tintColorImage forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
        [self setBackgroundImage:[self imageWithColor:[tintColor colorWithAlphaComponent:0.2]] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
        [self setBackgroundImage:tintColorImage forState:UIControlStateSelected|UIControlStateSelected barMetrics:UIBarMetricsDefault];
        [self setTitleTextAttributes:@{NSForegroundColorAttributeName: tintColor, NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateNormal];
        [self setDividerImage:tintColorImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
        self.layer.borderWidth = 1;
        self.layer.borderColor = [tintColor CGColor];
    }
}

- (UIImage *)imageWithColor: (UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return theImage;
}
@end
Colin Blake
la source
2
Je ne suis pas sûr que cela fonctionnera avec CGRectMake(0.0f, 0.0f, 1.0f, 1.0f): de mes tests avec Xcode 11 beta, rectdevait être de la même taille que les limites du contrôle segmenté.
Cœur
2
Depuis iOS13 beta 6, la teinte n'apparaissait pas sur un bouton sélectionné, j'ai donc dû ajouter une ligne: [self setTitleTextAttributes: @ {NSForegroundColorAttributeName: UIColor.blackColor, NSFontAttributeName: [UIFont systemFontOfSize: 13]} forState: UIContOfSize: 13]} forState: UIControlected];
Peter Johnson
Quand j'essaye d'utiliser ceci sur [[UISegmentedControl appearance] ensureiOS12Style]j'obtiens une exception. Une idée de ce qui se passe? Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSMethodSignature getArgumentTypeAtIndex:]: index (2) out of bounds [0, 1]'
Jeremy Hicks
13

À partir de Xcode 11 beta 3

Il y a maintenant la selectedSegmentTintColorpropriété sur UISegmentedControl.

Merci @rmaddy!


Réponse originale, pour Xcode 11 beta et beta 2

Existe-t-il une solution appropriée, utilisant des API publiques, qui ne nécessite pas de creuser dans la structure de sous-vue privée?

Avec Xcode 11.0 bêta, cela semble être un défi de le faire par les règles, car cela nécessite essentiellement de redessiner toutes les images d'arrière-plan pour chaque état par vous-même, avec des coins arrondis, de la transparence et resizableImage(withCapInsets:). Par exemple, vous devrez générer une image colorée similaire à:
entrez la description de l'image ici

Donc pour l'instant, la manière de creuser dans les sous-vues semble beaucoup plus facile:

class TintedSegmentedControl: UISegmentedControl {

    override func layoutSubviews() {
        super.layoutSubviews()

        if #available(iOS 13.0, *) {
            for subview in subviews {
                if let selectedImageView = subview.subviews.last(where: { $0 is UIImageView }) as? UIImageView,
                    let image = selectedImageView.image {
                    selectedImageView.image = image.withRenderingMode(.alwaysTemplate)
                    break
                }
            }
        }
    }
}

Cette solution appliquera correctement la couleur de teinte à la sélection, comme dans: entrez la description de l'image ici

Cœur
la source
1
Décerné parce que le temps presse, mais de préférence une solution qui n'implique pas de creuser dans la hiérarchie privée sera trouvée :)
Jonathan.
@Jonathan. Je vous remercie. Vous avez déjà la solution la plus proche qui n'implique pas de regarder la hiérarchie: car une fois que vous avez setBackgroundImageterminé .normal, vous devez définir toutes les autres images (y compris les autres états et setDividerImage), probablement avec certaines UIBezierPathet resizableImage(withCapInsets:), ce qui le rend trop complexe si nous voulons le design iOS 13 par ici.
Cœur
Ouais, idéalement, il sera corrigé dans une version bêta
Jonathan.
3
Cela n'est plus nécessaire à partir d'iOS 13b3. Il y a maintenant la selectedSegmentTintColorpropriété sur UISegmentedControl.
rmaddy
11

Réponse de la version Swift de @Ilahi Charfeddine:

if #available(iOS 13.0, *) {
   segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
   segmentedControl.selectedSegmentTintColor = UIColor.blue
} else {
   segmentedControl.tintColor = UIColor.blue
}
Vignan S
la source
10
if (@available(iOS 13.0, *)) {

    [self.segmentedControl setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont systemFontOfSize:13]} forState:UIControlStateSelected];
    [self.segmentedControl setSelectedSegmentTintColor:[UIColor blueColor]];

} else {

[self.segmentedControl setTintColor:[UIColor blueColor]];}
Ilahi Charfeddine
la source
7

iOS13 UISegmentController

comment utiliser:

segment.setOldLayout(tintColor: .green)

extension UISegmentedControl
{
    func setOldLayout(tintColor: UIColor)
    {
        if #available(iOS 13, *)
        {
            let bg = UIImage(color: .clear, size: CGSize(width: 1, height: 32))
             let devider = UIImage(color: tintColor, size: CGSize(width: 1, height: 32))

             //set background images
             self.setBackgroundImage(bg, for: .normal, barMetrics: .default)
             self.setBackgroundImage(devider, for: .selected, barMetrics: .default)

             //set divider color
             self.setDividerImage(devider, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)

             //set border
             self.layer.borderWidth = 1
             self.layer.borderColor = tintColor.cgColor

             //set label color
             self.setTitleTextAttributes([.foregroundColor: tintColor], for: .normal)
             self.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
        }
        else
        {
            self.tintColor = tintColor
        }
    }
}
extension UIImage {
    convenience init(color: UIColor, size: CGSize) {
        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        color.set()
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.fill(CGRect(origin: .zero, size: size))
        let image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        self.init(data: image.pngData()!)!
    }
}
Jigar Darji
la source
1
C'est la seule approche qui a fonctionné pour moi - les images d'arrière-plan. selectedSegmentTintColor n'a pas fonctionné pour une raison quelconque.
DenNukem
7

XCODE 11.1 et iOS 13

Basé sur la réponse de @Jigar Darji mais une mise en œuvre plus sûre.

Nous créons d'abord un initialiseur pratique disponible:

extension UIImage {

convenience init?(color: UIColor, size: CGSize) {
    UIGraphicsBeginImageContextWithOptions(size, false, 1)
    color.set()
    guard let ctx = UIGraphicsGetCurrentContext() else { return nil }
    ctx.fill(CGRect(origin: .zero, size: size))
    guard
        let image = UIGraphicsGetImageFromCurrentImageContext(),
        let imagePNGData = image.pngData()
        else { return nil }
    UIGraphicsEndImageContext()

    self.init(data: imagePNGData)
   }
}

Ensuite, nous étendons UISegmentedControl:

extension UISegmentedControl {

func fallBackToPreIOS13Layout(using tintColor: UIColor) {
    if #available(iOS 13, *) {
        let backGroundImage = UIImage(color: .clear, size: CGSize(width: 1, height: 32))
        let dividerImage = UIImage(color: tintColor, size: CGSize(width: 1, height: 32))

        setBackgroundImage(backGroundImage, for: .normal, barMetrics: .default)
        setBackgroundImage(dividerImage, for: .selected, barMetrics: .default)

        setDividerImage(dividerImage,
                        forLeftSegmentState: .normal,
                        rightSegmentState: .normal, barMetrics: .default)

        layer.borderWidth = 1
        layer.borderColor = tintColor.cgColor

        setTitleTextAttributes([.foregroundColor: tintColor], for: .normal)
        setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
    } else {
        self.tintColor = tintColor
    }
  }
}
FredFlinstone
la source
Parfait! Je vous remercie!
Senocico Stelian
5

Voici mon point de vue sur la réponse de Jonathan pour Xamarin.iOS (C #), mais avec des correctifs pour le dimensionnement de l'image. Comme pour le commentaire de Cœur sur la réponse de Colin Blake, j'ai fait toutes les images sauf le diviseur de la taille du contrôle segmenté. Le diviseur est 1xhauteur du segment.

public static UIImage ImageWithColor(UIColor color, CGSize size)
{
    var rect = new CGRect(0, 0, size.Width, size.Height);
    UIGraphics.BeginImageContext(rect.Size);
    var context = UIGraphics.GetCurrentContext();
    context.SetFillColor(color.CGColor);
    context.FillRect(rect);
    var image = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();
    return image;
}

// https://stackoverflow.com/a/56465501/420175
public static void ColorSegmentiOS13(UISegmentedControl uis, UIColor tintColor, UIColor textSelectedColor, UIColor textDeselectedColor)
{
    if (!UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
    {
        return;
    }

    UIImage image(UIColor color)
    {
        return ImageWithColor(color, uis.Frame.Size);
    }

    UIImage imageDivider(UIColor color)
    {
        return ImageWithColor(color, 1, uis.Frame.Height);
    }

    // Must set the background image for normal to something (even clear) else the rest won't work
    //setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
    uis.SetBackgroundImage(image(UIColor.Clear), UIControlState.Normal, UIBarMetrics.Default);

    // setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
    uis.SetBackgroundImage(image(tintColor), UIControlState.Selected, UIBarMetrics.Default);

    // setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
    uis.SetBackgroundImage(image(tintColor.ColorWithAlpha(0.2f)), UIControlState.Highlighted, UIBarMetrics.Default);

    // setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
    uis.SetBackgroundImage(image(tintColor), UIControlState.Highlighted | UIControlState.Selected, UIBarMetrics.Default);

    // setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
    // Change: support distinct color for selected/de-selected; keep original font
    uis.SetTitleTextAttributes(new UITextAttributes() { TextColor = textDeselectedColor }, UIControlState.Normal); //Font = UIFont.SystemFontOfSize(13, UIFontWeight.Regular)
    uis.SetTitleTextAttributes(new UITextAttributes() { TextColor = textSelectedColor, }, UIControlState.Selected); //Font = UIFont.SystemFontOfSize(13, UIFontWeight.Regular)

    // setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
    uis.SetDividerImage(imageDivider(tintColor), UIControlState.Normal, UIControlState.Normal, UIBarMetrics.Default);

    //layer.borderWidth = 1
    uis.Layer.BorderWidth = 1;

    //layer.borderColor = tintColor.cgColor
    uis.Layer.BorderColor = tintColor.CGColor;
}
t9mike
la source
3

Vous pouvez implémenter la méthode suivante

extension UISegmentedControl{
    func selectedSegmentTintColor(_ color: UIColor) {
        self.setTitleTextAttributes([.foregroundColor: color], for: .selected)
    }
    func unselectedSegmentTintColor(_ color: UIColor) {
        self.setTitleTextAttributes([.foregroundColor: color], for: .normal)
    }
}

Code d'utilisation

segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
Zain Anjum
la source
0

Bien que les réponses ci-dessus soient excellentes, la plupart d'entre elles ont une mauvaise couleur du texte à l'intérieur du segment sélectionné. J'ai créé une UISegmentedControlsous-classe que vous pouvez utiliser sur les appareils iOS 13 et pré-iOS 13 et utiliser la propriété tintColor comme vous le feriez sur les appareils pré-iOS 13.

    class LegacySegmentedControl: UISegmentedControl {
        private func stylize() {
            if #available(iOS 13.0, *) {
                selectedSegmentTintColor = tintColor
                let tintColorImage = UIImage(color: tintColor)
                setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
                setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
                setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
                setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
                setTitleTextAttributes([.foregroundColor: tintColor!, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)

                setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)
                layer.borderWidth = 1
                layer.borderColor = tintColor.cgColor

// Detect underlying backgroundColor so the text color will be properly matched

                if let background = backgroundColor {
                    self.setTitleTextAttributes([.foregroundColor: background, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
                } else {
                    func detectBackgroundColor(of view: UIView?) -> UIColor? {
                        guard let view = view else {
                            return nil
                        }
                        if let color = view.backgroundColor, color != .clear {
                            return color
                        }
                        return detectBackgroundColor(of: view.superview)
                    }
                    let textColor = detectBackgroundColor(of: self) ?? .black

                    self.setTitleTextAttributes([.foregroundColor: textColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .selected)
                }
            }
        }

        override func tintColorDidChange() {
            super.tintColorDidChange()
            stylize()
        }
    }

    fileprivate extension UIImage {
        public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
          let rect = CGRect(origin: .zero, size: size)
          UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
          color.setFill()
          UIRectFill(rect)
          let image = UIGraphicsGetImageFromCurrentImageContext()
          UIGraphicsEndImageContext()

          guard let cgImage = image?.cgImage else { return nil }
          self.init(cgImage: cgImage)
        }
    }

En utilisant la tintColorDidChangeméthode, nous nous assurons que la stylizeméthode sera appelée chaque fois que la tintColorpropriété change sur la vue de segment, ou sur l'une des vues sous-jacentes, ce qui est le comportement préféré sur iOS.

Résultat: entrez la description de l'image ici

Adam
la source
-2

Développez un peu la réponse de Jonathan au cas où vous ne voudriez pas de coins arrondis

extension UISegmentedControl {
    /// Tint color doesn't have any effect on iOS 13.
    func ensureiOS12Style(roundCorner: Bool = true) {
        if #available(iOS 13, *) {
            let tintColorImage = UIImage(color: tintColor)
            // Must set the background image for normal to something (even clear) else the rest won't work
            setBackgroundImage(UIImage(color: backgroundColor ?? .clear), for: .normal, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: .selected, barMetrics: .default)
            setBackgroundImage(UIImage(color: tintColor.withAlphaComponent(0.2)), for: .highlighted, barMetrics: .default)
            setBackgroundImage(tintColorImage, for: [.highlighted, .selected], barMetrics: .default)
            setTitleTextAttributes([.foregroundColor: tintColor, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: .regular)], for: .normal)
            setDividerImage(tintColorImage, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default)

            if !roundCorner {
                layer.masksToBounds = false

                let borderView = UIView()
                borderView.layer.borderWidth = 1
                borderView.layer.borderColor = UIColor.black.cgColor
                borderView.isUserInteractionEnabled = false
                borderView.translatesAutoresizingMaskIntoConstraints = false

                addSubview(borderView)

                NSLayoutConstraint(item: borderView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
                NSLayoutConstraint(item: borderView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
                NSLayoutConstraint(item: borderView, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1, constant: 0).isActive = true
                NSLayoutConstraint(item: borderView, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1, constant: 0).isActive = true
            }
        }
    }
}
GoNinja
la source