3

这个问题相当简单,但我在许多 UISegmentedControl 帖子中看不到任何真正满足我需求的东西:

环境:我有一组显示在 UISegmentedControl 中的目录。这是一个扁平的层次结构,目录很少,所以这是显示它们的最佳方式。

选择一个段会用该目录的内容填充下面的 UITableView。

我可以以编程方式选择给定的段,以便我可以根据需要选择适当的段。

效果很好。

问题:

其中一个目录是“默认”目录,它将包含已经存在的项目和新项目的混合。

我想给这个片段加上标记,这样就有一个指示它有多少新片段,这样人们就知道选择它,如果它还没有为他们选择的话。

这意味着我需要访问 UISegmentedControl 中的实际子视图和诸如此类的东西。

没那么容易。创建徽章是孩子的游戏。弄清楚将徽章放在哪里是成年人的事情。

看起来苹果故意隐藏对细分市场的直接访问。您只能影响整个控件。

有没有人对我如何只修改一个片段有任何建议,甚至找出那个片段在哪里?

widthForSegmentAtIndex :。函数似乎毫无价值,因为它是否会为您提供任何有用的信息是任意的。

4

3 回答 3

3

您知道 segmentedControl 的大小,也知道分段的数量。只需划分以获得每个段的宽度。它们都是相同的宽度。

然后将您的徽章放在 segmentedControl 顶部的适当 x 位置。

于 2013-10-01T22:52:38.323 回答
3

不,返回的值widthForSegmentAtIndex:不是任意的。正如您在文档中看到的,它返回段的宽度或 0.0,这意味着段是自动调整的。

有一种方法可以获取每个段的帧。

  1. 获取 UISegmentedControl 的宽度
  2. 询问每个段的宽度
    • 如果返回 0.0,则增加一个计数器
    • 否则从总宽度中删除段的宽度
  3. 将剩余宽度除以自动调整大小的段数,您就有了每个自动调整大小的段的大小。
  4. 您现在可以使用此信息来计算每个段的框架。

或在代码中:

据我在 iOS7 上看到的,段之间的“边界”不是段宽度的一部分。

CGFloat autosizedWidth = CGRectGetWidth(self.segment.bounds);
// iOS7 only?!
autosizedWidth -= (self.segment.numberOfSegments - 1); // ignore the 1pt. borders between segments


NSInteger numberOfAutosizedSegmentes = 0;
NSMutableArray *segmentWidths = [NSMutableArray arrayWithCapacity:self.segment.numberOfSegments];
for (NSInteger i = 0; i < self.segment.numberOfSegments; i++) {
    CGFloat width = [self.segment widthForSegmentAtIndex:i];
    if (width == 0.0f) {
        // auto sized
        numberOfAutosizedSegmentes++;
        [segmentWidths addObject:[NSNull null]];
    }
    else {
        // manually sized
        autosizedWidth -= width;
        [segmentWidths addObject:@(width)];
    }
}

CGFloat autoWidth = floorf(autosizedWidth/(float)numberOfAutosizedSegmentes);
for (NSInteger i = 0; i < [segmentWidths count]; i++) {
    id width = segmentWidths[i];
    if (width == [NSNull null]) {
        [segmentWidths replaceObjectAtIndex:i withObject:@(autoWidth)];
    }
}

CGFloat x = CGRectGetMinX(self.segment.frame);
for (NSInteger i = 0; i < [segmentWidths count]; i++) {
    NSNumber *width = segmentWidths[i];
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(x, CGRectGetMaxY(self.segment.frame) + 1, [width floatValue], 30)];
    view.backgroundColor = [UIColor colorWithHue:i/(float)[segmentWidths count] saturation:1 brightness:1 alpha:1];
    [self.view addSubview:view];
    x = CGRectGetMaxX(view.frame)+1;
}

这会产生以下结果:

在此处输入图像描述

我建议您不要将您的徽章添加为 UISegmentedControl 的子视图,您可以将其添加到 segmentedControl 的 superView 中。您的徽章应该基本上是 segmentedControl 的兄弟


请向 Apple 提交增强请求。它们不会让我们访问各个子视图,但它们至少可以告诉我们段的实际大小。

于 2013-10-01T22:55:47.293 回答
1

更新:我做了更多的调整,但我仍然没有完美,但这似乎给出了一个很好的近似值,给或取一个像素。

只要控件很小,那么上面的效果很好,但是有一些错误。可能是因为我没有在某个地方接收几个像素大小的值。

这是我在测试应用程序中所做的(注意:该项目已更新以具有更多测试控件)。

首先,我在 UISegmentedControl 类上声明了一个类扩展:

@interface UISegmentedControl (Overload)
- (NSArray*)mapUISegmentedControl;
@end

然后,我实现它,如下所示:

@implementation UISegmentedControl (Overload)
- (NSArray*)mapUISegmentedControl
{
    NSMutableArray  *segmentFrames = [NSMutableArray arrayWithCapacity:[self numberOfSegments]];
    NSInteger       numberOfAutosizedSegments = 0;
    double          ios7Coefficient = [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 ? 1 : 0;
    double          autosizedWidth = CGRectGetWidth ( [self bounds] ) - 1;

    for ( NSInteger i = 0; i < [self numberOfSegments]; i++ )
    {
        double width = [self widthForSegmentAtIndex:i];

        if ( width == 0.0f )
        {
            autosizedWidth -= 1;
            // If this is an auto-sized, we blank out the holding space for it.
            numberOfAutosizedSegments++;    // Add this to our count of auto-sized segments.
            [segmentFrames addObject:[NSNull null]];
        }
        else
        {
            // If the size has been explicitly set, we use that, and create a simple rect.
            autosizedWidth -= (width + ios7Coefficient);
            CGRect  frame = CGRectMake ( 0, 0, width, CGRectGetHeight ( [self bounds] ) );
            [segmentFrames addObject:[NSValue valueWithCGRect:frame]];
        }

    }

    // We divvy up the leftover space for the autoscales.
    double autoWidth = (autosizedWidth + ios7Coefficient) / (double)numberOfAutosizedSegments;

    double  x = 0;

    for ( NSInteger i = 0; i < [segmentFrames count]; i++ )
    {
        CGRect  frame = CGRectZero;
        double  width = 0;

        // If this is auto-sized (flagged by a null object), then make an autosized rect.
        if ( segmentFrames[i] == [NSNull null] )
        {
            width = ceil ( autoWidth - ios7Coefficient );
        }
        else    // Otherwise, use the rect they supplied.
        {
            width = CGRectGetWidth ( [(NSValue*)[segmentFrames objectAtIndex:i] CGRectValue] );
        }

        width += 1;

        frame = CGRectMake ( x, 0, width, CGRectGetHeight( [self bounds] ) );
        [segmentFrames replaceObjectAtIndex:i
                                 withObject:[NSValue valueWithCGRect:frame]
         ];

        // The x origin keeps up with the control.
        x += width;
    }

    return [NSArray arrayWithArray:segmentFrames];
}
@end

接下来,我在布局响应中调用它,如下所示:

@implementation DemoViewController

- (void)drawOverlays
{
    NSArray *segmentMap = [[self segmentedControl] mapUISegmentedControl];

    if ( ![self colorBand] )
    {
        _colorBand = [[NSMutableArray alloc] init];
    }
    else
    {
        for ( UIView *view in [self colorBand] )
        {
            [view removeFromSuperview];
        }
        [[self colorBand] removeAllObjects];
    }

    for ( NSInteger i = 0; i < [segmentMap count]; i++ )
    {
        CGRect  frame = [(NSValue*)[segmentMap objectAtIndex:i] CGRectValue];
        frame.size.height /= 2.0;
        frame.origin.y = [[self segmentedControl] frame].origin.y + [[self segmentedControl] frame].size.height;
        UIView *view = [[UIView alloc] initWithFrame:frame];
        view.backgroundColor = [UIColor colorWithHue:i/(float)[segmentMap count] saturation:1 brightness:1 alpha:0.5];
        [[self view] addSubview:view];
        [[self colorBand] addObject:view];
    }
}

- (void)viewDidLayoutSubviews
{
    [self drawOverlays];
}
@end

这在 iOS 6 和 7 中都有效,但有时似乎偶尔会出现半像素错误。

结果如下所示:

iOS6

IOS 7

于 2013-10-03T19:52:45.690 回答