Jak zmienić kolory segmentu w UISegmentedControl w iOS 13?

110

A UISegmentedControlma nowy wygląd w iOS 13, a istniejący kod zmieniający kolory segmentowanej kontrolki nie działa już tak, jak działał.

Przed iOS 13 można było ustawić tintColori, które będzie używane do obramowania wokół podzielonej na segmenty kontrolki, linii między segmentami i koloru tła wybranego segmentu. Następnie możesz zmienić kolor tytułów każdego segmentu, używając atrybutu koloru pierwszego planu z titleTextAttributes.

W iOS 13 tintColornic nie robi. Możesz ustawić podzieloną na segmenty kontrolkę, backgroundColoraby zmienić ogólny kolor podzielonej na segmenty kontrolki. Ale nie mogę znaleźć żadnego sposobu na zmianę koloru używanego jako tło wybranego segmentu. Ustawianie atrybutów tekstu nadal działa. Próbowałem nawet ustawić kolor tła tytułu, ale wpływa to tylko na tło tytułu, a nie na pozostałą część koloru tła wybranego segmentu.

Krótko mówiąc, jak zmodyfikować kolor tła aktualnie wybranego segmentu UISegmentedControlw iOS 13? Czy istnieje odpowiednie rozwiązanie, wykorzystujące publiczne API, które nie wymaga zagłębiania się w strukturę prywatnego podglądu?

W iOS 13 nie ma nowych właściwości dla UISegmentedControllub UIControli żadne zmiany w programie nie UIViewsą istotne.

rmaddy
źródło

Odpowiedzi:

134

Od iOS 13b3 jest teraz selectedSegmentTintColorwłączony UISegmentedControl.

Aby zmienić ogólny kolor podzielonej na segmenty kontrolki, użyj jej backgroundColor.

Aby zmienić kolor wybranego segmentu, użyj selectedSegmentTintColor.

Aby zmienić kolor / czcionkę niewybranych tytułów segmentów, użyj setTitleTextAttributesstanu .normal/ UIControlStateNormal.

Aby zmienić kolor / czcionkę wybranych tytułów segmentów, użyj setTitleTextAttributesze stanem .selected/ UIControlStateSelected.

Jeśli utworzysz posegmentowaną kontrolkę z obrazami, jeśli obrazy są utworzone jako obrazy szablonu, to segmentowana kontrolka tintColorzostanie użyta do pokolorowania obrazów. Ale to jest problem. Jeśli ustawisz ten tintColorsam kolor co selectedSegmentTintColorwtedy obraz nie będzie widoczny w wybranym segmencie. Jeśli ustawisz ten tintColorsam kolor co backgroundColor, obrazy na niewybranych segmentach nie będą widoczne. Oznacza to, że podzielona na segmenty kontrolka z obrazami musi mieć 3 różne kolory, aby wszystko było widoczne. Lub możesz użyć obrazów innych niż szablon i nie ustawiać tintColor.

W systemie iOS 12 lub wcześniejszym wystarczy ustawić segmentowe sterowanie tintColorlub polegać na ogólnym kolorze odcienia aplikacji.

rmaddy
źródło
Jak ustawić kontroler segmentu bez granic? Nie widzę tego ustawienia w iOS 13. Wcześniej ustawienie tintcolor wystarczało, aby uzyskać kontrolę segmentów bez obramowania.
Deepak Sharma
Dodaj kolor obramowania itp., Aby wszyscy mogli znaleźć wszystkie problemy związane z kolorem segmentów zostaną tutaj rozwiązane. co mówi? :)
Yogesh Patel
1
@YogeshPatel A co z kolorem obramowania? W iOS 13 nie ma koloru obramowania, aw iOS 12 jest ustawiony, z tintColorktórym jest już objęty odpowiedzią.
rmaddy
@rmaddy Ustawiłem to [segmentedControl.layer setBorderColor: [[UIColor whiteColor] CGColor]]; [segmentedControl.layer setBorderWidth: 0.5]; daje mi kolor obramowania i obramowania w iOS 13.
Yogesh Patel
1
Och, ta granica. Dotyczy to każdego widoku, a nie tylko segmentowej kontroli. To wykracza poza zakres pierwotnego pytania i tej odpowiedzi. Twój komentarz wystarczy.
rmaddy
48

Od Xcode 11 beta 3

Teraz selectedSegmentTintColornieruchomość jest włączona UISegmentedControl.

Zobacz odpowiedź rmaddy


Aby przywrócić wygląd iOS 12

Nie udało mi się zabarwić koloru wybranego segmentu, mam nadzieję, że zostanie to naprawione w nadchodzącej wersji beta.

Ustawienie obrazu tła wybranego stanu nie działa bez ustawienia obrazu tła stanu normalnego (co usuwa wszystkie style iOS 13)

Ale udało mi się przywrócić wygląd iOS 12 (lub wystarczająco blisko, nie byłem w stanie przywrócić promienia narożnika do mniejszego rozmiaru).

Nie jest to idealne rozwiązanie, ale jasnobiały, segmentowy element sterujący wygląda nieco nie na miejscu w naszej aplikacji.

(Nie zdawałem sobie sprawy, że UIImage(color:)jest to metoda rozszerzająca w naszej bazie kodu. Ale kod do jej zaimplementowania znajduje się w Internecie)

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

Obraz przedstawiający efekt powyższego kodu

Jonathan.
źródło
To może być dobre obejście. Nie miałem jeszcze okazji tego wypróbować, ale czy wymaga to również wywołania, setTitleTextAttributesaby tytuł wybranego segmentu miał biały kolor?
rmaddy
Hmm, wygląda na to, że powinno, ale pozornie nie. Nie mam dostępu do tego bankomatu użycia kodu, ale obraz po lewej jest utworzony z tym kodem.
Jonathan.
8
stackoverflow.com/a/33675160/5790492 dla rozszerzenia UIImage (kolor :).
Nik Kov
1
Byłoby miło, gdybyś dodał rozszerzenie UIImage, w ten sposób twoja odpowiedź nie jest kompletna iho
FredFlinstone
1
@VityaShurapov jego ustawienie, gdy jest zarówno podświetlone, jak i wybrane, nie jest to tablica przekazanych stanów, ale raczej zestaw opcji, co oznacza, że ​​wartości są łączone w celu utworzenia nowego stanu.
Jonathan.
37

IOS 13 i Swift 5.0 (Xcode 11.0) Kontrola segmentów w 100% działa

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

 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
źródło
8
czy próbowałeś ustawić tło jako białe? dla mnie nadchodzi szaro
Ronit
@ItanHant, którego Xcode i Swift języka używasz?
Maulik Patel
@Ronit ofcourse !! spróbuję, ale proszę powiedz mi, jaki typ wyjścia jest teraz wyświetlany
Maulik Patel
swift 5, xcode 11.3! pokazuje, czego chce! nie ten, którego chcę :)
Itan Hant
16

Wypróbowałem to obejście i działa świetnie. Oto wersja 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
źródło
2
Nie jestem pewien, czy to zadziała CGRectMake(0.0f, 0.0f, 1.0f, 1.0f): z moich testów z Xcode 11 beta, rectmusiał być tego samego rozmiaru, co granice segmentowanej kontroli.
Cœur
2
Od iOS13 beta 6 tintcolor nie pojawił się na wybranym przycisku, więc musiałem dodać linię: [self setTitleTextAttributes: @ {NSForegroundColorAttributeName: UIColor.blackColor, NSFontAttributeName: [UIFont systemFontOfSize: 13]} forState: UIControl];
Peter Johnson
Kiedy próbuję użyć tego na [[UISegmentedControl appearance] ensureiOS12Style], pojawia się wyjątek. Masz jakiś pomysł, co się dzieje? Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSMethodSignature getArgumentTypeAtIndex:]: index (2) out of bounds [0, 1]'
Jeremy Hicks
13

Od Xcode 11 beta 3

Teraz selectedSegmentTintColornieruchomość jest włączona UISegmentedControl.

Dziękuję @rmaddy!


Oryginalna odpowiedź dla Xcode 11 beta i beta 2

Czy istnieje odpowiednie rozwiązanie wykorzystujące publiczne API, które nie wymaga zagłębiania się w strukturę prywatnego podglądu?

W przypadku Xcode 11.0 beta wydaje się wyzwaniem zrobienie tego zgodnie z zasadami, ponieważ zasadniczo wymaga to samodzielnego przerysowania wszystkich obrazów tła dla każdego stanu, z zaokrąglonymi rogami, przezroczystością i resizableImage(withCapInsets:). Na przykład musiałbyś wygenerować kolorowy obraz podobny do:
wprowadź opis obrazu tutaj

Na razie więc zagłębianie się w subviews wydaje się znacznie łatwiejsze:

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

To rozwiązanie poprawnie zastosuje kolor odcienia do zaznaczenia, jak w: wprowadź opis obrazu tutaj

Cœur
źródło
1
Nagroda przyznana, ponieważ czas ucieka, ale najlepiej będzie znaleźć rozwiązanie, które nie wymaga sięgania po prywatną hierarchię :)
Jonathan.
@Jonathan. Dziękuję Ci. Masz już najbliższe rozwiązanie, które nie wymaga patrzenia na hierarchię: ponieważ raz setBackgroundImageza .normalto musisz ustawić wszystkie inne obrazy (w tym inne stany i setDividerImage), prawdopodobnie z niektórymi UIBezierPathi resizableImage(withCapInsets:), co sprawia, że ​​jest to zbyt skomplikowane, jeśli chcemy projekt iOS 13 tą drogą.
Cœur
Tak, najlepiej byłoby to naprawić w wersji beta
Jonathan.
3
Nie jest to już potrzebne od iOS 13b3. Teraz selectedSegmentTintColornieruchomość jest włączona UISegmentedControl.
rmaddy
11

Szybka wersja odpowiedzi @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
źródło
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
źródło
7

iOS13 UISegmentController

jak używać:

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
źródło
1
To jedyne podejście, które zadziałało dla mnie - obrazy tła. selectedSegmentTintColor z jakiegoś powodu nie działa.
DenNukem
7

XCODE 11.1 i iOS 13

Na podstawie odpowiedzi @Jigar Darji, ale bezpieczniejsza implementacja.

Najpierw tworzymy dostępny inicjalizator wygody:

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

Następnie rozszerzamy 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
źródło
Idealny! Dziękuję Ci!
Senocico Stelian
5

Oto moje podejście do odpowiedzi Jonathana dla platformy Xamarin.iOS (C #), ale z poprawkami dotyczącymi rozmiaru obrazu. Podobnie jak w przypadku komentarza Cœura na temat odpowiedzi Colina Blake'a, wykonałem wszystkie obrazy poza dzielnikiem wielkości segmentowanej kontrolki. Dzielnik to 1x wysokość segmentu.

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
źródło
3

Możesz zaimplementować następującą metodę

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

Kod użytkowania

segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
Zain Anjum
źródło
2

Chociaż powyższe odpowiedzi są świetne, większość z nich ma nieprawidłowy kolor tekstu w wybranym segmencie. Utworzyłem UISegmentedControlpodklasę, której można używać na urządzeniach z systemem iOS 13 i starszym niż iOS 13 i używać właściwości tintColor tak, jak na urządzeniach z systemem starszym niż 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)
        }
    }

Korzystając z tintColorDidChangemetody, zapewniamy, że stylizemetoda zostanie wywołana za każdym razemtintColor właściwość zmieni się w widoku segmentu lub w dowolnym z widoków bazowych, co jest preferowanym zachowaniem w systemie iOS.

Wynik: wprowadź opis obrazu tutaj

Adam
źródło
-2

Rozwiń trochę odpowiedź Jonathana na wypadek, gdybyś nie chciał zaokrąglać rogów

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
źródło