获取特定标签栏项的框架

时间:2011-06-12 23:38:59

标签: iphone ios uitabbarcontroller uitabbaritem

有没有办法在UITabBarItem中找到特定UITabBar的框架?

具体来说,我想创建一个图像“落入”其中一个标签的动画,类似于例如删除邮件中的电子邮件,或在iTunes应用程序中购买曲目。所以我需要动画的目标坐标。

据我所知,没有公共API可以获得坐标,但是对此并不错。除此之外,我将不得不使用给定标签相对于标签栏框架的索引来猜测坐标。

12 个答案:

答案 0 :(得分:24)

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

  1. UITabBarButton视图不一定是有序的。例如,如果iPhone上有超过5个选项卡并重新排列选项卡,则视图可能会出现故障。
  2. 如果使用超过5个选项卡,则越界索引仅表示该选项卡位于“更多”选项卡后面。在这种情况下,没有理由使用断言失败,只需使用最后一个选项卡的框架。
  3. 所以我稍微更改了他的代码,我想出了这个:

    + (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;
    }
    

答案 1 :(得分:16)

与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和之前的版本。

答案 2 :(得分:10)

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

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

答案 3 :(得分:8)

在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
}

答案 4 :(得分:5)

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

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

进入数组后,根据原点x对其进行排序,然后按正确顺序排列标签栏按钮。

答案 5 :(得分:4)

在Swift 4.2中:

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
    }

}

答案 6 :(得分:3)

我将在我的简单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中的rect,找到该rect的中心并通过hitTest:withEvent挖掘出该点的视图。

答案 7 :(得分:2)

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)
}

答案 8 :(得分:1)

快捷键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]更改为所需的值

答案 9 :(得分:0)

您不需要任何私有API,只需使用UITabbar属性itemWidthitemSpacing即可。设置以下两个值:

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;

答案 10 :(得分:0)

初始化标签栏时,为其添加标签:

let tabItem = UITabBarItem(title: "my title", image: nil, tag: 1000)

然后您可以使用以下方法获取标签栏:

let myTabItem = tabBarController?.tabBar.viewWithTag(1000)

答案 11 :(得分:0)

redead's答案肯定有效,因此请对其进行投票。

我需要向tabBar添加活动指示器,并且需要在窗口内添加tabBar的矩形,因此我必须在其答案中添加一些其他代码:

// 1. get the frame for the first tab like in his answer
guard let firstTab = tabBarController?.tabBar.items?[0].valueForKey("view") as? UIView else { return }
print("firstTabframe: \(firstTab.frame)")

// 2. get a reference to the window
guard let window = UIApplication.shared.keyWindow else { return }

// 3. get the firstTab's rect inside the window
let firstTabRectInWindow = firstTab.convert(firstTab.frame, to: window)
print("firstTabRectInWindow: \(firstTabRectInWindow)")