UIToolbar的动画调整大小导致在iOS< 5.1上剪切背景

时间:2012-04-25 17:55:20

标签: ipad animation uitoolbar

我已经实现了一个自定义拆分视图控制器 - 原则上 - 运行良好。

然而,有一个方面是工作是预期的,这是版本5.1之前iOS上工具栏的调整大小动画 - 如果存在:

在对UIToolbar进行子类化以覆盖其layoutSubviews方法后,动画更改主内容区域的宽度会导致工具栏项按预期移动。然而,工具栏的背景 - 按预期进行动画处理。

相反,它的宽度会立即变为新值,导致在增加宽度时显示背景。

以下是我认为我使用的代码的相关部分 - 所有非常标准的东西,尽可能少的魔法/ hackery:

// From the implementation of my Split Layout View Class:
- (void)setAuxiliaryViewHidden:(BOOL)hide animated:(BOOL)animated completion:(void (^)(BOOL isFinished))completion
{
auxiliaryViewHidden_ = hide;
    if (!animated)
    {
        [self layoutSubviews];
        if (completion)
            completion(YES);

        return;
    }

    // I've tried it with and without UIViewAnimationOptionsLayoutSubviews -- didn't change anything...
    UIViewAnimationOptions easedRelayoutStartingFromCurrentState = UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState;
    [UIView animateWithDuration:M_1_PI delay:0.0 options:easedRelayoutStartingFromCurrentState animations:^{
        [self layoutSubviews];
    } completion:completion];
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    // tedious layout work to calculate the frames for the main- and auxiliary-content views

    self.mainContentView.frame = mainContentFrame; // <= This currently has the toolbar, but...
    self.auxiliaryContentView.frame = auxiliaryContentFrame; // ...this one could contain one, as well.
}

// The complete implementation of my UIToolbar class:
@implementation AnimatableToolbar

static CGFloat sThresholdSelectorMargin = 30.;

- (void)layoutSubviews
{
    [super layoutSubviews];

    // walk the subviews looking for the views that represent toolbar items 
    for (UIView *subview in self.subviews)
    {
        NSString *className = NSStringFromClass([subview class]);
        if (![className hasPrefix:@"UIToolbar"]) // not a toolbar item view
            continue;

        if (![subview isKindOfClass:[UIControl class]]) // some other private class we don't want to f**k around with…
            continue;

        CGRect frame = [subview frame];
        BOOL isLeftmostItem = frame.origin.x <= sThresholdSelectorMargin;
        if (isLeftmostItem)
        {
            subview.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
            continue;
        }

        BOOL isRightmostItem = (CGRectGetMaxX(self.bounds) - CGRectGetMaxX(frame)) <= sThresholdSelectorMargin;
        if (!isRightmostItem)
        {
            subview.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;

            continue;
        }

        subview.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
    }
}

@end

我在InterfaceBuilder中设置了工具栏的类,我知道这个代码被调用,就像我说的那样,在iOS 5.1上一切正常。

我必须支持iOS启动版本4.2,但是......

非常感谢任何关于我所缺少的帮助/提示。

2 个答案:

答案 0 :(得分:1)

据我所知,您的方法只适用于iOS SDK&gt; 5.实际上,iOS SDK 5引入了以明确方式操纵UIToolbar背景的可能性(参见setBackgroundImage:forToolbarPosition:barMetrics和相对getter方法)。

在iOS SDK 4中,UIToolbar对象没有_UIToolbarBackground子视图,因此您无法在layoutSubviews实施中移动它。要验证这一点,请添加如下跟踪:

for (UIView *subview in self.subviews)
{
    NSLog(@"FOUND SUBVIEW: %@", [subview description]);

在iOS 4和5上运行代码,您将看到我的意思。

总而言之,问题的解决方案在于在iOS 4和iOS 5下以两种不同的方式处理背景。具体来说,在iOS 4上,您可以尝试以下方法:

  1. 将子视图添加到充当背景视图的自定义UIToolbar

    [toolbar insertSubview:backgroundView atIndex:0];

  2. 组:

    toolbar.backgroundColor = [UIColor clearColor];

    以便UIToolbar背景颜色不会干扰;

  3. 在您的layoutSubviews方法中与其他子视图一起围绕此背景子视图进行动画处理,就像您正在做的那样;

  4. 当然,对于iOS 5来说,没有什么可以阻止你使用相同的背景子视图,只有你应该注意的是,在第1步,子视图应该插入索引1(即,在现有背景的顶部)。

    希望这会有所帮助。

答案 1 :(得分:0)

由于我认为这对其他人有用,我会把我的解决方案放在这里以供参考:

根据塞尔吉奥的建议,我在视图层次结构中插入了一个额外的UIImageView。但是因为我希望这可以使用默认的工具栏样式,所以我需要跳过几个环节:

  1. 只要tintColor发生变化,就需要动态生成图像。
  2. 在iOS 5.0.x上,工具栏背景是另一个视图。
  3. 为了解决这个问题,我结束了......

    1. 实施+load以设置是否需要执行任何操作的静态BOOL。 (对于5.1之前的版本,解析-[UIDevice systemVersion]。)。
    2. 为图片视图stretchableBackground添加(延迟加载)属性。如果我的静态标记为nil,则视图为NO。否则创建的视图宽度为[UIScreen mainScreen]的两倍,向左偏移该宽度的一半,并可调整高度和右边距,并插入到索引0处的工具栏中。
    3. 覆盖setTintColor:。每当发生这种情况时,我都会致电super__updateBackground
    4. 实施了一种方法__updateBackground
      • 当工具栏响应backgroundImageForToolbarPosition:barMetrics:时,获取的第一个子视图不是我们的stretchableBackground。使用该视图图层的contents属性填充stretchableBackground的{​​{1}}属性并返回。
      • 如果工具栏没有响应该选择器,
        1. 使用image获取32位RGBA CGBitmapContextCreate(),它是一个像素宽,并且与工具栏一样高,乘以屏幕的比例。 (使用kCGImageAlphaPremultipliedLast处理设备RGB颜色空间...)
        2. 按照该高度翻译CTM并按比例/ -scale缩放以从UIKit过渡到CG-Coordinates并将视图的图层绘制到该上下文中。 (如果你没有这样做,你的图像将始终是透明的空白......)
        3. 从该上下文创建UIImage并将其设置为CGContextRef的图像。
    5. 请注意,当使用不同的背景图像作为纵向和横向或不缩放的图像时,iOS 5.0.x 的此修复程序将无法正常工作 - 尽管可以通过配置图像视图进行调整不同...