在iOS 5中自定义UISegmentedControl

27

以下是我的问题。我正在通过以下方式设置背景和分隔符图像来自定义UISegmentedControl:

[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

当我尝试在viewDidLoad中选择第一个分段时

self.segmentedControl.selectedIndex = 1;

我得到了以下奇怪的结果:

enter image description here

而不是:

enter image description here

有人知道这是否是一个bug,我应该如何提供bug报告?如果不是,那么我的代码可能存在什么问题呢?


1
我理解的是否正确,您的控件在视图加载后手动点击其中一个段时看起来很好?还是它总是像第一张图片中那样? - Desmond
1
看起来控件在启动时使用了错误的分隔图像(它正在使用未选择/未选择的图像而不是未选择/已选择的图像)。在您的应用程序生命周期中的哪个具体点上设置UISegmentedControl的外观? - Desmond
我正在viewDidLoad方法中设置外观。 - nimeshdesai
我之前自己使用过这个,没有遇到问题。分段控件的值在开始时是通过 NSUserDefaults 程序加载的,但我以前从未遇到过这种情况。虽然 SDK 可能存在漏洞,但在这种情况下应该会有更多人遇到此问题。如果您可以发布更多代码,可能会有所帮助。 - Desmond
2
在iOS 6中似乎存在一个类似的(但另一个)bug。当视图首次加载时(如果使用两个段),视图会使用错误的分隔符图像,直到您以编程方式切换到另一个段,然后再切回来。如果您在viewWillAppear中执行此操作,则不起作用,但如果您在viewDidAppear中执行此操作,则可以正常工作! - Accatyyc
显示剩余6条评论
6个回答

10

经过一些测试并尝试了几个不同的自定义位置后,我认为这可能确实是一个 bug。

即使是使用非常简单的直线 UISegmentedControl,这就是我得到的结果(使用 Xcode 4.3.1,iOS 5.1):

启动并选择代码中的中间元素后: 启动并选择代码中的中间元素

用户点击其他地方并再次点击中间元素后: 用户点击其他地方并再次点击中间元素

我使用了 3 像素宽的图片作为分隔符,并使用 1 像素宽的图片作为背景。

编辑:我想我找到了一个解决办法:尝试排队选择该元素的指令,而不是在 viewDidLoad 中执行它,像这样:

dispatch_async(dispatch_get_main_queue(),^{
   self.segmentedControl.selectedSegmentIndex = 1;
});

在我的上面的示例中,排队那个指令使它正常工作。


我会接受你的答案,因为它对我有点用。 所有这些控件都在我的设置页面中,我还有另一个控件来更改主题。所以,如果我改变主题(不同的分段控件图像集),我会显示一个加载屏幕,之后再次显示视图并且它被破坏了。但是,如果我移动到另一个视图(此时设置视图已被释放),并且如果我返回到设置视图(其中它再次被分配),则分段控件将正常显示。 - nimeshdesai
你把这个语句放在哪里了? - Asem H.
这对我有用,但我不确定是否喜欢必须排队,感觉像是一个hack。 - Chris
有人为此提交了RDAR吗? - jackslash
这种方法可以工作,但会破坏我的TableView。如果我在viewDidLoad中把它放在最后调用,就得不到任何行。 - Ilker Baltaci

4

我认为你的图像 CapInsets 是错误的,请参考 http://www.raywenderlich.com/4344/user-interface-customization-in-ios-5 中的示例。

下面是教程中的一些代码,可以快速参考:

UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_uns.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
UIImage *segmentSelectedUnselected = [UIImage imageNamed:@"segcontrol_sel-uns.png"];
UIImage *segUnselectedSelected = [UIImage imageNamed:@"segcontrol_uns-sel.png"];
UIImage *segmentUnselectedUnselected = [UIImage imageNamed:@"segcontrol_uns-uns.png"];

[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected forLeftSegmentState:UIControlStateSelected rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segUnselectedSelected forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

1

你尝试过设置可调整大小的图片吗?

例如:

UIImage *segmentSelected = [[UIImage imageNamed:@"SegmentSelectedImage"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];

这是我创建图像的方式。 - nimeshdesai

1

经过多次实验,我找到了解决问题的方法。

你的问题是由于分段控件中错误的宽度设置引起的。

首先,我们需要在设置各个部分的宽度之前进行用户界面定制。

其次,我们需要计算出分隔符的宽度,这非常重要。在进行自定义时,分隔符是UISegmentedControl元素的一部分,而不是覆盖层。我们还应该计算分隔符的宽度。

第三点,当我们使用设定宽度的方法来设置各个部分的宽度时,段的宽度不需要包括分隔符的宽度。

如果你按照以上规则操作,你会得到一个完美的自定义UISegmentedControl。


我该如何获取分隔符的宽度?所有段落的宽度是否需要相同? - nimeshdesai
分隔符的宽度是实际分隔符图像的宽度。每个段的宽度可以不同。在进行UISegmentedControl自定义时,您需要进行更好的计算。 - user403015
1
我有同样的问题,而且我不明白这应该如何工作。在我的代码中,我从未手动设置过段落的宽度。UIAppearance代理应该负责所有这一切。@user403015,你能否详细说明一下你是如何让它工作的? - Sebastian
如果我正在使用可调整大小的图像,那么“计算宽度”到底有什么区别?顺便问一下,有人解决了这个问题吗? - Asem H.
只需计算除数并将计算出的值保存到文件中吗? - pronebird
分割图像是否会使段落的边距膨胀? - pronebird

0

我曾经遇到过同样的问题,但是我通过不同的方式解决了它。我只是注释掉了这行代码:

self.pageController.segmentedControlStyle = UISegmentedControlStyleBar;

并且分段栏将其视图更改为正常状态。


这与楼主的问题有何关联? - user577537
我遇到了同样的问题,并且按照我在消息中指出的方式解决了它。 - Wert1go
你能否编辑你的回答,以便在这种情况下更清晰地链接到问题? - user577537

0

你可能想在viewDidLoad之前尝试初始化外观设置。在我看过的演示中,通常是在application:didFinishLaunchingWithOptions:中完成的,但在加载包含自定义视图的nib之前的任何时点都应该可以工作。


我尝试了这个,但它不起作用!我认为这可能是SDK的一个错误。 - nimeshdesai
有人在没有留下评论的情况下对我们的答案进行了负评。我对此感到非常失望。我为你点赞。干杯。 - Lorenzo B
你看到我的评论了吗?祝你有美好的一天 ;) - Lorenzo B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接