rma*_*ddy 68 uisegmentedcontrol uikit ios ios13
A UISegmentedControl
在iOS 13中具有新外观,并且现有代码更改分段控件的颜色不再像以前那样起作用。
在iOS 13之前,您可以设置tintColor
和,以用于分段控件周围的边框,分段之间的线条以及所选分段的背景颜色。然后,您可以使用带有的前景色属性更改每个句段标题的颜色titleTextAttributes
。
在iOS 13下,tintColor
什么都不做。您可以设置分段控件backgroundColor
以更改分段控件的整体颜色。但是我找不到任何方法来更改用作所选段背景的颜色。设置文本属性仍然有效。我什至尝试设置标题的背景色,但这只会影响标题的背景,而不会影响所选片段的其余背景色。
简而言之,您如何修改UISegmentedControl
iOS 13中当前选定的段的背景颜色?是否有使用公共API的适当解决方案,而无需深入研究私有子视图结构?
在iOS 13中,没有针对UISegmentedControl
或的新属性,UIControl
并且这些更改UIView
均不相关。
rma*_*ddy 91
由于iOS的13B3的,现在有一个selectedSegmentTintColor
上UISegmentedControl
。
要更改分段控件的整体颜色,请使用backgroundColor
。
要更改所选段的颜色,请使用selectedSegmentTintColor
。
要更改未选择的段标题的颜色/字体,请使用/ setTitleTextAttributes
状态。.normal
UIControlStateNormal
要更改所选段标题的颜色/字体,请使用/ setTitleTextAttributes
状态。.selected
UIControlStateSelected
如果使用图像创建分段控件,则将图像创建为模板图像时,tintColor
将使用分段控件为图像着色。但这有一个问题。如果将设置tintColor
为与相同的颜色,selectedSegmentTintColor
则图像将在所选段中不可见。如果将设置tintColor
为与相同的颜色backgroundColor
,则未选择的线段上的图像将不可见。这意味着您的带有图像的分段控件必须使用3种不同的颜色才能使所有内容可见。或者,您可以使用非模板图像而不设置tintColor
。
在iOS 12或更早版本下,只需设置细分控件tintColor
或依靠应用程序的整体色彩即可。
Jon*_*an. 33
现在有
selectedSegmentTintColor
物业UISegmentedControl
。
我无法为所选部分着色,希望它会在即将发布的Beta版中得到修复。
如果不设置正常状态的背景图像,则设置选定状态的背景图像将不起作用(这将删除所有iOS 13样式)
但是我能够使它恢复到iOS 12的外观(或者说距离足够近,我无法将拐角半径恢复为较小的尺寸)。
这并不理想,但是明亮的白色分段控件在我们的应用中看起来有点不合适。
(没有意识到这UIImage(color:)
是我们代码库中的扩展方法。但是,实现它的代码是在网上发布的)
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
Jig*_*rji 11
iOS13 UISegmentController
如何使用:
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()!)!
}
}
Run Code Online (Sandbox Code Playgroud)
Cœu*_*œur 10
现在有
selectedSegmentTintColor
物业UISegmentedControl
。
谢谢@rmaddy!
是否有使用公共API的适当解决方案,而无需深入研究私有子视图结构?
对于Xcode 11.0 beta,按规则进行操作似乎是一个挑战,因为它基本上要求您自己绘制每个州的所有背景图像,并带有圆角,透明度和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
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
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]];}
Run Code Online (Sandbox Code Playgroud)
Xcode 11.1 和 iOS 13
基于@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)
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们扩展 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
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 7
我已经尝试了解决方法,对我来说效果很好。这是Objective-C版本:
@interface UISegmentedControl (Common)
- (void)ensureiOS12Style;
@end
Run Code Online (Sandbox Code Playgroud)
@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
Run Code Online (Sandbox Code Playgroud)
虽然上面的答案很好,但大多数人都将所选段内的文本颜色弄错了。我创建了UISegmentedControl
一个子类,您可以在 iOS 13 和 iOS 13 之前的设备上使用它,并像在 iOS 13 之前的设备上一样使用tintColor 属性。
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)
}
}
Run Code Online (Sandbox Code Playgroud)
使用tintColorDidChange
方法,我们确保每次段视图或任何底层视图上的属性更改时stylize
都会调用该方法,这是 iOS 上的首选行为。tintColor
SwiftUIPicker
缺少一些基本选项。对于尝试在 iOS 13 或 14 的 SwiftUI 中使用 SegmentedPickerStyle() 自定义 Picker 的人来说,最简单的选择是用于UISegmentedControl.appearance()
全局设置外观。这是一个可以调用来设置外观的示例函数。
func setUISegmentControlAppearance() {
UISegmentedControl.appearance().selectedSegmentTintColor = .white
UISegmentedControl.appearance().backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
}
Run Code Online (Sandbox Code Playgroud)
但是,UISegmentedControl.appearance()
如果您想要具有不同设置的多个控件,则全局设置外观选项并不是很好。另一种选择是实施UIViewRepresentable
for UISegmentedControl
. 这是一个示例,设置原始问题中询问的属性并设置.apportionsSegmentWidthsByContent = true
为奖励。希望这能为您节省一些时间......
struct MyPicker: UIViewRepresentable {
@Binding var selection: Int // The type of selection may vary depending on your use case
var items: [Any]?
class Coordinator: NSObject {
let parent: MyPicker
init(parent: MyPicker) {
self.parent = parent
}
@objc func valueChanged(_ sender: UISegmentedControl) {
self.parent.selection = Int(sender.selectedSegmentIndex)
}
}
func makeCoordinator() -> MyPicker.Coordinator {
Coordinator(parent: self)
}
func makeUIView(context: Context) -> UISegmentedControl {
let picker = UISegmentedControl(items: self.items)
// Any number of other UISegmentedControl settings can go here
picker.selectedSegmentTintColor = .white
picker.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.1)
picker.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
picker.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
picker.apportionsSegmentWidthsByContent = true
// Make sure the coordinator updates the picker when the value changes
picker.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged)
return picker
}
func updateUIView(_ uiView: UISegmentedControl, context: Context) {
uiView.selectedSegmentIndex = self.selection
}
}
Run Code Online (Sandbox Code Playgroud)
这是我对 Jonathan. 对 Xamarin.iOS (C#) 的回答,但修复了图像大小。与 Cœur 对 Colin Blake 回答的评论一样,我将除分隔线以外的所有图像都设为分段控件的大小。分隔线是段的 1x 高度。
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;
}
Run Code Online (Sandbox Code Playgroud)
您可以实施以下方法
extension UISegmentedControl{
func selectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .selected)
}
func unselectedSegmentTintColor(_ color: UIColor) {
self.setTitleTextAttributes([.foregroundColor: color], for: .normal)
}
}
Run Code Online (Sandbox Code Playgroud)
使用代码
segmentControl.unselectedSegmentTintColor(.white)
segmentControl.selectedSegmentTintColor(.black)
Run Code Online (Sandbox Code Playgroud)
@Ilahi Charfeddine的Swift版本答案:
if #available(iOS 13.0, *) {
segmentedControl.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
segmentedControl.selectedSegmentTintColor = UIColor.blue
} else {
segmentedControl.tintColor = UIColor.blue
}
Run Code Online (Sandbox Code Playgroud)
IOS 13和Swift 5.0(Xcode 11.0)Segment Control 100%Working
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
}
Run Code Online (Sandbox Code Playgroud)