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

Posted

技术标签:

【中文标题】UIToolbar 的动画调整大小导致在 iOS <5.1 上剪切背景【英文标题】:Animated Resize of UIToolbar Causes Background to be Clipped on iOS <5.1 【发布时间】:2012-04-25 17:55:19 【问题描述】:

我已经实现了一个自定义的拆分视图控制器——原则上——效果很好。

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

在继承 UIToolbar 以覆盖其layoutSubviews 方法后,对我的主要内容区域的宽度进行动画更改会导致工具栏项按预期移动。但是,工具栏的背景没有按预期设置动画。

相反,它的宽度会立即更改为新值,从而在增加宽度的同时显示背景。

以下是我认为我使用的代码的相关部分——都是非常标准的东西,尽可能少的魔法/黑客:

// 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 上一切正常。

不过,我必须从 4.2 版开始支持 iOS……

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

【问题讨论】:

【参考方案1】:

据我所知,您的方法只能在 iOS SDK > 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 上,您可以尝试以下方法:

    向您的自定义UIToolbar 添加一个子视图,充当背景视图:

    [toolbar insertSubview:backgroundView atIndex:0];

    设置:

    toolbar.backgroundColor = [UIColor clearColor];

    这样UIToolbar的背景颜色就不会干扰了;

    在您的 layoutSubviews 方法中,与其他子视图一起围绕此背景子视图制作动画,就像您正在做的那样;

当然,没有什么能阻止您在 iOS 5 中使用相同的背景子视图,唯一需要注意的是,在第 1 步中,子视图应插入到索引 1(即,在现有背景之上)。

希望这会有所帮助。

【讨论】:

射击,我忘了评论布局子视图:我正在以这样的方式布置我的工具栏项目视图 - 否则 - 动画不流畅,但也会跳转到它们的新位置。跨度> 我不确定我是否完全理解您的评论,抱歉……我的提示有帮助吗? 我的layoutSubviews 不打算移动工具栏的背景。它只是缓解了在设置工具栏的框架时——即使在 UIViewAnimation(块)内——工具栏项“跳转”到它们的新位置的问题。在 iOS 我没有时间测试您的建议,但由于我的布局技巧有效,我认为没有理由不插入子视图。没有其他答案表明存在替代方法,因此赏金归您所有!【参考方案2】:

因为我认为这对其他人有用,所以我将我的解决方案放在这里以供参考:

根据 sergio 的建议,我在视图层次结构中插入了一个额外的 UIImageView。但由于我希望它与默认的工具栏样式一起使用,所以我需要跳过几圈:

    需要在tintColor 更改时动态生成图像。 在 iOS 5.0.x 上,工具栏背景是一个附加视图。

为了解决这个问题,我最终...

    实现+load 来设置我是否需要做任何事情的静态BOOL。 (为 5.1 之前的版本解析 -[UIDevice systemVersion])。 为图像视图stretchableBackground 添加一个(延迟加载的)属性。如果我的静态标志为NO,则视图将为nil。否则,将创建具有两倍于[UIScreen mainScreen] 宽度的视图,向左偏移该宽度的一半,高度和右边距可调整大小,并插入到索引 0 处的工具栏。 覆盖setTintColor:。每当发生这种情况时,我都会致电super__updateBackground。 实现了一个方法__updateBackground: 当工具栏响应backgroundImageForToolbarPosition:barMetrics: 时,获取不是我们的stretchableBackground 的第一个子视图。使用该视图层的contents 属性填充stretchableBackgroundimage 属性并返回。 如果工具栏没有响应该选择器,
      使用CGBitmapContextCreate() 获得一个32 位RGBA CGContextRef,其宽度为1 个像素,高度为工具栏乘以屏幕比例。 (使用 kCGImageAlphaPremultipliedLast 来处理设备 RGB 颜色空间……) 将 CTM 平移该高度并通过 scale/-scale 对其进行缩放以从 UIKit 过渡到 CG 坐标并将视图的图层绘制到该上下文中。 (如果你不这样做,你的图像将永远是透明的空白......) 从该上下文创建一个 UIImage 并将其设置为 stretchableBackground 的图像。

请注意,iOS 5.0.x 的此修复将无法按预期工作,当使用不同的背景图像用于纵向和横向或不缩放的图像时 - 尽管可以通过配置图像视图进行调整不一样……

【讨论】:

以上是关于UIToolbar 的动画调整大小导致在 iOS <5.1 上剪切背景的主要内容,如果未能解决你的问题,请参考以下文章

iOS - UIToolbar 从后台返回后调整大小

在 UIToolbar 中的 UIBarButtonItem 上更改 tintColor 会导致按钮消失并从左侧动画化

iOS - 目前的模态视图控制器会导致一些动画伪影(iPhone)

调整 UIViewController 中的视图大小

使用UIToolbar调整UIView的大小不会同时调整大小

iOS – UIToolbar 的动画故障