Wie ändere ich die Farben eines Segments in einem UISegmentedControl in iOS 13?

107

A UISegmentedControlhat ein neues Erscheinungsbild in iOS 13 und vorhandener Code zum Ändern der Farben des segmentierten Steuerelements funktioniert nicht mehr wie bisher.

Vor iOS 13 konnten Sie das tintColorund festlegen , das für den Rahmen um das segmentierte Steuerelement, die Linien zwischen den Segmenten und die Hintergrundfarbe des ausgewählten Segments verwendet wird. Anschließend können Sie die Farbe der Titel jedes Segments mithilfe des Attributs Vordergrundfarbe mit ändern titleTextAttributes.

Unter iOS 13 tintColormacht das nichts. Sie können die segmentierten Steuerelemente so einstellen backgroundColor, dass die Gesamtfarbe des segmentierten Steuerelements geändert wird. Ich kann jedoch keine Möglichkeit finden, die Farbe zu ändern, die als Hintergrund für das ausgewählte Segment verwendet wird. Das Festlegen der Textattribute funktioniert weiterhin. Ich habe sogar versucht, die Hintergrundfarbe des Titels festzulegen, aber das wirkt sich nur auf den Hintergrund des Titels aus, nicht auf den Rest der Hintergrundfarbe des ausgewählten Segments.

Kurz gesagt, wie können Sie die Hintergrundfarbe des aktuell ausgewählten Segments eines UISegmentedControlin iOS 13 ändern ? Gibt es eine geeignete Lösung mit öffentlichen APIs, bei der nicht in die private Unteransichtsstruktur gegraben werden muss?

In iOS 13 gibt es keine neuen Eigenschaften für UISegmentedControloder UIControlund keine der Änderungen in UIViewist relevant.

rmaddy
quelle

Antworten:

133

Ab iOS 13b3 gibt es jetzt ein selectedSegmentTintColorOn UISegmentedControl.

Um die Gesamtfarbe des segmentierten Steuerelements zu ändern, verwenden Sie seine backgroundColor.

Verwenden Sie zum Ändern der Farbe des ausgewählten Segments selectedSegmentTintColor.

Verwenden Sie den setTitleTextAttributesStatus .normal/, um die Farbe / Schriftart der nicht ausgewählten Segmenttitel zu ändern UIControlStateNormal.

Verwenden Sie den setTitleTextAttributesStatus .selected/, um die Farbe / Schriftart der ausgewählten Segmenttitel zu ändern UIControlStateSelected.

Wenn Sie ein segmentiertes Steuerelement mit Bildern erstellen und die Bilder als Vorlagenbilder erstellt werden, werden die segmentierten Steuerelemente tintColorzum Färben der Bilder verwendet. Das hat aber ein Problem. Wenn Sie tintColordie gleiche Farbe wie einstellen , wird das selectedSegmentTintColorBild im ausgewählten Segment nicht angezeigt. Wenn Sie tintColordie gleiche Farbe wie einstellen backgroundColor, werden die Bilder in den nicht ausgewählten Segmenten nicht angezeigt. Dies bedeutet, dass Ihr segmentiertes Steuerelement mit Bildern 3 verschiedene Farben verwenden muss, damit alles sichtbar ist. Oder Sie können Bilder ohne Vorlage verwenden und die nicht festlegen tintColor.

Stellen Sie unter iOS 12 oder früher einfach die segmentierten Steuerelemente ein tintColoroder verlassen Sie sich auf die Gesamtfarbe der App.

rmaddy
quelle
Wie setzen wir den Segmentcontroller ohne Rahmen? In iOS 13 wird hierfür keine Einstellung angezeigt. Früher reichte die Einstellung von Farbton aus, um eine randlose Segmentsteuerung zu erhalten.
Deepak Sharma
Bitte fügen Sie Rahmenfarbe usw. hinzu, damit alle Segmente finden können. Farbprobleme werden hier behoben. was sagt ? :)
Yogesh Patel
1
@YogeshPatel Was ist mit der Rahmenfarbe? In iOS 13 gibt es keine Rahmenfarbe und in iOS 12 ist sie festgelegt, mit tintColorder bereits in der Antwort behandelt wird.
rmaddy
@rmaddy Ich habe diese [segmentedControl.layer setBorderColor: [[UIColor whiteColor] CGColor]] eingestellt; [segmentedControl.layer setBorderWidth: 0.5]; es gibt mir Rand und Randfarbe in iOS 13.
Yogesh Patel
1
Oh, diese Grenze. Dies gilt für jede Ansicht, nicht nur für ein segmentiertes Steuerelement. Das geht über den Rahmen der ursprünglichen Frage und dieser Antwort hinaus. Ihr Kommentar ist genug.
rmaddy
47

Ab Xcode 11 Beta 3

Es ist jetzt die selectedSegmentTintColorEigenschaft auf UISegmentedControl.

Siehe rmaddys Antwort


So erhalten Sie das Erscheinungsbild von iOS 12 zurück

Ich konnte die Farbe des ausgewählten Segments nicht tönen, hoffentlich wird es in einer kommenden Beta behoben.

Das Festlegen des Hintergrundbilds des ausgewählten Status funktioniert nicht, ohne das Hintergrundbild des normalen Status festzulegen (wodurch das gesamte iOS 13-Styling entfernt wird).

Aber ich konnte es wieder auf das iOS 12-Erscheinungsbild bringen (oder fast genug, ich konnte den Eckenradius nicht auf seine kleinere Größe zurücksetzen).

Es ist nicht ideal, aber ein hellweißes segmentiertes Steuerelement sieht in unserer App etwas fehl am Platz aus.

(Wusste nicht, dass UIImage(color:)es sich um eine Erweiterungsmethode in unserer Codebasis handelt. Der Code für die Implementierung befindet sich jedoch im Internet.)

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
        }
    }
}

Bild zeigt die Wirkung des obigen Codes

Jonathan.
quelle
Dies könnte eine gute Problemumgehung sein. Ich hatte noch keine Gelegenheit, dies zu versuchen, aber erfordert dies auch einen Anruf setTitleTextAttributes, damit der Titel des ausgewählten Segments eine weiße Farbe hat?
rmaddy
Hmm, es sieht so aus, als ob es sollte, aber anscheinend nicht. Ich habe keinen Zugriff auf diesen Code-Verwendungs-Atm, aber das Bild links wird mit diesem Code erstellt.
Jonathan.
8
stackoverflow.com/a/33675160/5790492 für die Erweiterung UIImage (Farbe :).
Nik Kov
1
Es wäre schön, wenn Sie die UIImage-Erweiterung hinzufügen würden. Auf diese Weise ist Ihre Antwort nicht vollständig. Iho
FredFlinstone
1
@VityaShurapov setzt es, wenn es sowohl hervorgehoben als auch ausgewählt ist. Es ist kein Array von übergebenen Zuständen, sondern ein Optionssatz, was bedeutet, dass die Werte kombiniert werden, um einen neuen Zustand zu erstellen.
Jonathan.
36

IOS 13 und Swift 5.0 (Xcode 11.0) Segmentsteuerung 100% funktionsfähig

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

 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
quelle
7
Haben Sie versucht, den Hintergrund als weiß festzulegen? für mich kommt es grau
Ronit
@ItanHant welchen xcode und welche schnelle sprache verwendest du?
Maulik Patel
@ Ronit natürlich !! Ich werde versuchen, aber bitte sagen Sie mir, welche Art der Ausgabe jetzt angezeigt wird
Maulik Patel
Swift 5, Xcode 11.3! es zeigt was es will! nicht der, den ich will :)
Itan Hant
15

Ich habe die Problemumgehung ausprobiert und sie funktioniert hervorragend für mich. Hier ist die Objective-C-Version:

@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
quelle
2
Ich bin mir nicht sicher, ob es funktionieren wird CGRectMake(0.0f, 0.0f, 1.0f, 1.0f): Aus meinen Tests mit Xcode 11 Beta rectmusste die gleiche Größe wie die Grenzen des segmentierten Steuerelements stammen.
Cœur
2
Seit iOS13 Beta 6 wurde die Farbtönung nicht auf einer ausgewählten Schaltfläche angezeigt, daher musste ich eine Zeile hinzufügen: [self setTitleTextAttributes: @ {NSForegroundColorAttributeName: UIColor.blackColor, NSFontAttributeName: [UIFont systemFontOfSize: 13] onState;
Peter Johnson
Wenn ich versuche, dies zu verwenden, [[UISegmentedControl appearance] ensureiOS12Style]erhalte ich eine Ausnahme. Irgendeine Idee, was los ist? Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSMethodSignature getArgumentTypeAtIndex:]: index (2) out of bounds [0, 1]'
Jeremy Hicks
13

Ab Xcode 11 Beta 3

Es ist jetzt die selectedSegmentTintColorEigenschaft auf UISegmentedControl.

Danke @rmaddy!


Ursprüngliche Antwort für Xcode 11 Beta und Beta 2

Gibt es eine geeignete Lösung mit öffentlichen APIs, bei der nicht in die private Unteransichtsstruktur gegraben werden muss?

Mit Xcode 11.0 Beta scheint es eine Herausforderung zu sein, dies nach den Regeln zu tun, da grundsätzlich alle Hintergrundbilder für jeden Status selbst neu gezeichnet werden müssen, mit runden Ecken, Transparenz und resizableImage(withCapInsets:). Zum Beispiel müssten Sie ein farbiges Bild erzeugen, ähnlich wie:
Geben Sie hier die Bildbeschreibung ein

Im Moment scheint es also viel einfacher zu sein, in die Unteransichten zu graben:

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
                }
            }
        }
    }
}

Diese Lösung wendet die Farbtonfarbe korrekt auf die Auswahl an, wie in: Geben Sie hier die Bildbeschreibung ein

Cœur
quelle
1
Wird vergeben, weil die Zeit knapp wird, aber vorzugsweise wird eine Lösung gefunden, bei der nicht in die private Hierarchie gegraben wird :)
Jonathan.
@ Jonathan. Danke dir. Sie haben bereits die nächste Lösung geht , der nicht in der Hierarchie suchen: denn wenn man setBackgroundImagefür .normalSie alle anderen Bilder (einschließlich anderer Staaten und setzen müssen setDividerImage), wahrscheinlich mit einigen UIBezierPathund resizableImage(withCapInsets:), was es zu komplex macht , wenn wir die iOS 13 Design wollen diesen Weg.
Cœur
Ja, idealerweise wird es in einer Beta behoben
Jonathan.
3
Dies wird ab iOS 13b3 nicht mehr benötigt. Es ist jetzt die selectedSegmentTintColorEigenschaft auf UISegmentedControl.
rmaddy
11

Schnelle Version von @Ilahi Charfeddine Antwort:

if #available(iOS 13.0, *) {
   segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
   segmentedControl.selectedSegmentTintColor = UIColor.blue
} else {
   segmentedControl.tintColor = UIColor.blue
}
Vignan S.
quelle
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
quelle
7

iOS13 UISegmentController

wie benutzt man:

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
quelle
1
Dies ist der einzige Ansatz, der für mich funktioniert hat - die Hintergrundbilder. selectedSegmentTintColor hat aus irgendeinem Grund nicht funktioniert.
DenNukem
7

XCODE 11.1 & iOS 13

Basierend auf der Antwort von @Jigar Darji, aber einer sichereren Implementierung.

Wir erstellen zunächst einen fehlerhaften Convenience-Initialisierer:

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)
   }
}

Dann erweitern wir 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
quelle
Perfekt! Danke dir!
Senocico Stelian
5

Hier ist meine Sicht auf Jonathans Antwort für Xamarin.iOS (C #), aber mit Korrekturen für die Bildgröße. Wie bei Cœurs Kommentar zu Colin Blakes Antwort habe ich alle Bilder außer dem Teiler so groß wie das segmentierte Steuerelement gemacht. Der Teiler ist 1x Höhe des Segments.

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
quelle
3

Sie können die folgende Methode implementieren

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

Verwendungscode

segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
Zain Anjum
quelle
0

Die obigen Antworten sind zwar großartig, aber die meisten von ihnen haben die falsche Textfarbe innerhalb des ausgewählten Segments. Ich habe eine UISegmentedControlUnterklasse erstellt, die Sie auf Geräten mit iOS 13 und vor iOS 13 verwenden können, und verwende die Eigenschaft tintColor wie auf Geräten vor 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)
        }
    }

Mithilfe der tintColorDidChangeMethode stellen wir sicher, dass die stylizeMethode jedes Mal aufgerufen wird, wenn sich die tintColorEigenschaft in der Segmentansicht oder in einer der zugrunde liegenden Ansichten ändert. Dies ist das bevorzugte Verhalten unter iOS.

Ergebnis: Geben Sie hier die Bildbeschreibung ein

Adam
quelle
-2

Erweitern Sie die Antwort von Jonathan ein wenig, falls Sie keine runden Ecken wollen

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
quelle