获取特定标签栏项目的框架

| 有没有办法在
UITabBar
中找到特定
UITabBarItem
的帧? 具体来说,我想在其中一个标签中创建图像“掉落”的动画,类似于删除邮件中的电子邮件,或在iTunes应用程序中购买曲目。所以我需要动画的目标坐标。 据我所知,尚无公共API来获取坐标,但乐于出错。简而言之,我将不得不使用给定标签相对于标签栏框架的索引来估算坐标。     
已邀请:
        Imre的实现缺少几个重要的重要细节。 UITabBarButton视图不一定按顺序排列。例如,如果您在iPhone上有超过5个选项卡,并且重新排列了选项卡,则视图可能会混乱。 如果使用5个以上的选项卡,则越界索引仅表示该选项卡在\“ more \”选项卡的后面。在这种情况下,没有理由使用断言失败,只需使用最后一个选项卡的框架即可。 所以我稍微修改了他的代码,我想到了:
+ (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;
}
    
        与UITabBar中的选项卡栏项目关联的子视图属于UITabBarButton类。通过使用两个选项卡记录UITabBar的子视图:
for (UIView* view in self.tabBar.subviews)
{
    NSLog(view.description);
}
你得到:
<_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>>
基于此,解决方案很简单。我编写的尝试方法如下:
+ (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;
}
应该注意的是UITabBar的结构(子视图)和类UITabBarButton本身不是公共API的一部分,因此从理论上讲,它可以在任何新的iOS版本中进行更改,而无需事先通知。不过,他们不太可能会更改此类详细信息,并且在iOS 5-6和早期版本中可以正常使用。     
        另一个可能但草率的解决方案是:
guard let view = self.tabBarVC?.tabBar.items?[0].valueForKey(\"view\") as? UIView else 
{
    return
}
let frame = view.frame
    
        在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
}
    
        通过扩展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
    }

}
    
        标签栏按钮是唯一启用了用户交互的子视图。而不是UITabBarButton进行检查,以避免违反隐藏的API。
for (UIView* view in self.tabBar.subviews)
    {
        if( [view isUserInteractionEnabled] )
        {
            [myMutableArray addObject:view];
        }
}
一旦进入数组,请根据原点x对其进行排序,然后按真实顺序显示选项卡栏按钮。     
        我将在简单的UITabBarController场景中添加对我有用的东西,所有内容都是合法的,但它假设项目之间的间距相等。在iOS7下,它返回一个UITabBarButton的实例,但是如果您将其用作UIView *,则您实际上不在乎它是什么,也没有明确声明该类。返回视图的框架是您要查找的框架:
-(UIView*)viewForTabBarItemAtIndex:(NSInteger)index {

    CGRect tabBarRect = self.tabBarController.tabBar.frame;
    NSInteger buttonCount = self.tabBarController.tabBar.items.count;
    CGFloat containingWidth = tabBarRect.size.width/buttonCount;
    CGFloat originX = containingWidth * index ;
    CGRect containingRect = CGRectMake( originX, 0, containingWidth, self.tabBarController.tabBar.frame.size.height );
    CGPoint center = CGPointMake( CGRectGetMidX(containingRect), CGRectGetMidY(containingRect));
    return [ self.tabBarController.tabBar hitTest:center withEvent:nil ];
}
它的作用是在按钮所在的UITabBar中计算矩形,找到此矩形的中心,然后通过hitTest:withEvent挖掘出该点的视图。     
        Swift + iOS 11
private func frameForTabAtIndex(index: Int) -> CGRect {
    guard let tabBarSubviews = tabBarController?.tabBar.subviews else {
        return CGRect.zero
    }
    var allItems = [UIView]()
    for tabBarItem in tabBarSubviews {
        if tabBarItem.isKind(of: NSClassFromString(\"UITabBarButton\")!) {
            allItems.append(tabBarItem)
        }
    }
    let item = allItems[index]
    return item.superview!.convert(item.frame, to: view)
}
    
        斯威夫特5: 只需将其放入用于处理tabBar的View Controller中即可。
guard let tab1frame = self.tabBar.items![0].value(forKey: \"view\") as? UIView else {return}
然后像这样使用:
let tab1CentreXanc = tab1frame.centerXAnchor
let tab1CentreY = tab1frame.centerYAnchor
let tab1FRAME = tab1frame (centerX and Y and other parameters such as \".origins.x or y\"
希望能有所帮助。 如果要引用其他选项卡中的参数,只需将索引从[0]更改为所需的值即可     
        您不需要任何私有API,只需使用UITabbar属性
itemWidth
itemSpacing
。设置这两个值,如下所示:
NSInteger tabBar.itemSpacing = 10;
tabBar.itemWidth = ([UIScreen mainScreen].bounds.size.width - self.tabBarController.tabBar.itemSpacing * (itemsCount - 1)) /itemsCount;
请注意,这不会影响用于UITabBarItem的图像和标题的大小或位置。 然后您可以得到
ith
项的中心x,如下所示:
CGFloat itemCenterX = (tabBar.itemWidth + tabBar.itemSpacing) * ith + tabBar.itemWidth / 2;
    
        初始化标签栏时,给它一个标签:
let tabItem =  UITabBarItem(title: \"my title\", image: nil, tag: 1000)
然后,您可以使用以下方法获取标签栏:
let myTabItem = tabBarController?.tabBar.viewWithTag(1000)
    

要回复问题请先登录注册