获取特定标签栏项的框架

Dan*_*son 34 iphone uitabbarcontroller uitabbaritem ios

有没有办法找到特定的框架UITabBarItemUITabBar

具体来说,我想创建一个图像"下降"到其中一个选项卡的动画,类似于删除邮件中的电子邮件或购买iTunes应用程序中的曲目.所以我需要动画的目标坐标.

据我所知,没有公共API来获取坐标,但是对此却不满意.除此之外,我将不得不使用给定标签相对于标签栏框架的索引来猜测坐标.

Mat*_*uch 24

Imre的实施缺少一些重要的细节.

  1. UITabBarButton视图不一定按顺序排列.例如,如果iPhone上有超过5个选项卡并重新排列选项卡,则视图可能会出现故障.
  2. 如果使用超过5个选项卡,则超出范围索引仅表示该选项卡位于"更多"选项卡后面.在这种情况下,没有理由使用断言失败,只需使用最后一个选项卡的框架.

所以我稍微更改了他的代码,我想出了这个:

+ (CGRect)frameForTabInTabBar:(UITabBar*)tabBar withIndex:(NSUInteger)index
{
    NSMutableArray *tabBarItems = [NSMutableArray arrayWithCapacity:[tabBar.items count]];
    for (UIView *view in tabBar.subviews) {
        if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")] && [view respondsToSelector:@selector(frame)]) {
            // check for the selector -frame to prevent crashes in the very unlikely case that in the future
            // objects thar don't implement -frame can be subViews of an UIView
            [tabBarItems addObject:view];
        }
    }
    if ([tabBarItems count] == 0) {
        // no tabBarItems means either no UITabBarButtons were in the subView, or none responded to -frame
        // return CGRectZero to indicate that we couldn't figure out the frame
        return CGRectZero;
    }

    // sort by origin.x of the frame because the items are not necessarily in the correct order
    [tabBarItems sortUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
        if (view1.frame.origin.x < view2.frame.origin.x) {
            return NSOrderedAscending;
        }
        if (view1.frame.origin.x > view2.frame.origin.x) {
            return NSOrderedDescending;
        }
        NSAssert(NO, @"%@ and %@ share the same origin.x. This should never happen and indicates a substantial change in the framework that renders this method useless.", view1, view2);
        return NSOrderedSame;
    }];

    CGRect frame = CGRectZero;
    if (index < [tabBarItems count]) {
        // viewController is in a regular tab
        UIView *tabView = tabBarItems[index];
        if ([tabView respondsToSelector:@selector(frame)]) {
            frame = tabView.frame;
        }
    }
    else {
        // our target viewController is inside the "more" tab
        UIView *tabView = [tabBarItems lastObject];
        if ([tabView respondsToSelector:@selector(frame)]) {
            frame = tabView.frame;
        }
    }
    return frame;
}
Run Code Online (Sandbox Code Playgroud)

  • 鉴于UITabBarButton是一个私有iOS类,Apple的AppStore审核流程越来越彻底,我认为根本没有任何对该类的引用是谨慎的 - 甚至通过NSClassFromString().但它是一个UIControl子类,所有UIControl都实现 - (CGRect)框架.所以你可以用这个消除一堆条件:`if([view isKindOfClass:[UIControl class]])[tabBarItems addObject:view];` (15认同)

Imr*_*nyi 17

与UITabBar中的选项卡栏项关联的子视图属于UITabBarButton类.通过使用两个选项卡记录UITabBar的子视图:

for (UIView* view in self.tabBar.subviews)
{
    NSLog(view.description);
}
Run Code Online (Sandbox Code Playgroud)

你得到:

<_UITabBarBackgroundView: 0x6a91e00; frame = (0 0; 320 49); opaque = NO; autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x6a91e90>> - (null)
<UITabBarButton: 0x6a8d900; frame = (2 1; 156 48); opaque = NO; layer = <CALayer: 0x6a8db10>>
<UITabBarButton: 0x6a91b70; frame = (162 1; 156 48); opaque = NO; layer = <CALayer: 0x6a8db40>>
Run Code Online (Sandbox Code Playgroud)

基于此,解决方案有点微不足道.我为解决这个问题而编写的方法如下:

+ (CGRect)frameForTabInTabBar:(UITabBar*)tabBar withIndex:(NSUInteger)index
{
    NSUInteger currentTabIndex = 0;

    for (UIView* subView in tabBar.subviews)
    {
        if ([subView isKindOfClass:NSClassFromString(@"UITabBarButton")])
        {
            if (currentTabIndex == index)
                return subView.frame;
            else
                currentTabIndex++;
        }
    }

    NSAssert(NO, @"Index is out of bounds");
    return CGRectNull;
}
Run Code Online (Sandbox Code Playgroud)

应该注意的是,UITabBar的结构(子视图)和类UITabBarButton本身不是公共API的一部分,因此理论上它可以在没有事先通知的情况下在任何新的iOS版本中进行更改.然而,他们不太可能改变这样的细节,并且它适用于iOS 5-6和之前的版本.


小智 12

另一个可能但草率的解决方案如下:

guard let view = self.tabBarVC?.tabBar.items?[0].valueForKey("view") as? UIView else 
{
    return
}
let frame = view.frame
Run Code Online (Sandbox Code Playgroud)


bui*_*ded 9

在Swift 4.2中:

private func frameForTab(atIndex index: Int) -> CGRect {
    var frames = view.subviews.compactMap { (view:UIView) -> CGRect? in
        if let view = view as? UIControl {
            return view.frame
        }
        return nil
    }
    frames.sort { $0.origin.x < $1.origin.x }
    if frames.count > index {
        return frames[index]
    }
    return frames.last ?? CGRect.zero
}
Run Code Online (Sandbox Code Playgroud)


Lau*_*ert 7

通过扩展UITabBar

extension UITabBar {

    func getFrameForTabAt(index: Int) -> CGRect? {
        var frames = self.subviews.compactMap { return $0 is UIControl ? $0.frame : nil }
        frames.sort { $0.origin.x < $1.origin.x }
        return frames[safe: index]
    }

}

extension Collection {

    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }

}
Run Code Online (Sandbox Code Playgroud)


Tig*_*ing 5

标签栏按钮是唯一启用了用户交互的子视图.检查这个而不是UITabBarButton以避免违反隐藏的API.

for (UIView* view in self.tabBar.subviews)
    {
        if( [view isUserInteractionEnabled] )
        {
            [myMutableArray addObject:view];
        }
}
Run Code Online (Sandbox Code Playgroud)

进入数组后,根据原点x对其进行排序,您将按正确的顺序选择标签栏按钮.