UISegmentedControl
はiOS 13で新しい外観になり、セグメント化されたコントロールの色を変更する既存のコードは機能しなくなりました。
IOS 13より前のバージョンでは、tintColor
を設定できました。これは、セグメント化されたコントロールの周りの境界線、セグメント間の線、および選択したセグメントの背景色に使用されていました。次に、titleTextAttributes
で前景色の属性を使用して、各セグメントのタイトルの色を変更できます。
IOS 13では、tintColor
は何もしません。セグメント化されたコントロールのbackgroundColor
を設定して、セグメント化されたコントロールの全体的な色を変更できます。しかし、選択したセグメントの背景として使用される色を変更する方法が見つかりません。テキスト属性の設定は引き続き機能します。タイトルの背景色を設定してみましたが、これはタイトルの背景にのみ影響し、選択したセグメントの残りの背景色には影響しません。
つまり、iOS 13でUISegmentedControl
の現在選択されているセグメントの背景色をどのように変更しますか?プライベートサブビュー構造を掘り下げる必要のない、パブリックAPIを使用する適切なソリューションはありますか?
IOS 13にはUISegmentedControl
またはUIControl
の新しいプロパティはなく、UIView
の変更は関係ありません。
IOS 13b3以降、selectedSegmentTintColor
にUISegmentedControl
が追加されました。
セグメント化されたコントロールの全体的な色を変更するには、そのbackgroundColor
を使用します。
選択したセグメントの色を変更するには、selectedSegmentTintColor
を使用します。
選択されていないセグメントタイトルの色/フォントを変更するには、.normal
/setTitleTextAttributes
の状態でUIControlStateNormal
を使用します。
選択したセグメントタイトルの色/フォントを変更するには、.selected
/setTitleTextAttributes
の状態でUIControlStateSelected
を使用します。
画像付きのセグメント化されたコントロールを作成する場合、画像がテンプレート画像として作成されると、セグメント化されたコントロールのtintColor
が画像の色付けに使用されます。しかし、これには問題があります。 tintColor
をselectedSegmentTintColor
と同じ色に設定すると、選択したセグメントで画像が表示されなくなります。 tintColor
をbackgroundColor
と同じ色に設定すると、選択されていないセグメントの画像は表示されなくなります。つまり、画像付きのセグメント化されたコントロールは、すべてを表示するために3つの異なる色を使用する必要があります。または、テンプレート以外のイメージを使用して、tintColor
を設定しないこともできます。
IOS 12以前では、セグメント化されたコントロールのtintColor
を設定するか、アプリの全体的なティントカラーに依存します。
現在、
selectedSegmentTintColor
にUISegmentedControl
プロパティがあります。
rmaddy's answer を参照してください
選択したセグメントの色に色を付けることができませんでした。うまくいけば、次のベータ版で修正されるでしょう。
選択した状態の背景画像の設定は、通常の状態の背景画像を設定しないと機能しません(すべてのiOS 13スタイルが削除されます)
しかし、私はそれをiOS 12の外観に戻すことができました(または、十分に近いと、角の半径を小さいサイズに戻すことができませんでした)。
これは理想的ではありませんが、明るい白いセグメント化されたコントロールは、アプリで少し場違いに見えます。
(UIImage(color:)
がコードベースの拡張メソッドであることを理解していませんでした。しかし、それを実装するコードは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
}
}
}
IOS 13およびSwift 5.0(Xcode 11.0)セグメント制御100%機能
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
}
現在、
selectedSegmentTintColor
にUISegmentedControl
プロパティがあります。
@rmaddyありがとうございます!
パブリックAPIを使用して、プライベートサブビュー構造を掘り下げる必要のない適切なソリューションはありますか?
Xcode 11.0ベータでは、基本的に、丸い角、透明度、resizableImage(withCapInsets:)
を使用して、すべての状態のすべての背景画像を自分で再描画する必要があるため、ルールに従って行うのは難しいようです。たとえば、次のようなカラー画像を生成する必要があります。
そのため、今のところ、サブビューを掘り下げる方法の方がはるかに簡単に思えます。
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
}
}
}
}
}
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]];}
私は回避策を試してみましたが、それは私にとってはうまくいきます。これが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
@Ilahi Charfeddine回答のSwiftバージョン:
if #available(iOS 13.0, *) {
segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
segmentedControl.selectedSegmentTintColor = UIColor.blue
} else {
segmentedControl.tintColor = UIColor.blue
}
Xamarin.iOS(C#)に対するJonathan。の答えを以下に示しますが、画像サイズの修正が含まれています。 Colin Blakeの回答に対するCœurのコメントと同様に、分割器を除くすべての画像をセグメント化されたコントロールのサイズにしました。仕切りはセグメントの高さの1倍です。
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;
}
次のメソッドを実装できます
extension UISegmentedControl{
func selectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .selected)
}
func unselectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .normal)
}
}
使用法コード
segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
iOS 13 UIセグメントコントローラー
使い方:
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()!)!
}
}
XCODE 11.1およびiOS 1
@Jigar Darjiの回答に基づいていますが、より安全な実装です。
まず、失敗する便利なイニシャライザを作成します。
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)
}
}
次に、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
}
}
}