2011-06-12 47 views
25

有没有办法在UITabBar中找到特定UITabBarItem的框架?获取特定标签栏项目的框架

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

据我所知,没有公共的API来获得坐标,但会喜欢错误的。简而言之,我将不得不使用给定选项卡的索引相对于选项卡栏框架来猜测坐标。

+0

喂,你有没有发现在swift..If此解决方案是的,那么你可以请帮助我.. – Rahul 2017-04-18 12:20:56

回答

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

+0

+ 1为iPad,但此解决方案不适用于iPhone/iPod。这给出了错误的坐标。 – iLearner 2013-04-29 14:36:56

+0

适用于iPhone的iOS 9.2相当不错。我用''''UIControl'''(@Greg Combs建议)作为一种子视图类比较。 – rafalkitta 2016-03-16 15:04:25

22

伊姆雷的实施失踪了几个重要的细节。

  1. UITabBarButton视图不一定按顺序排列。例如,如果iPhone上有5个以上的选项卡和重新排列的选项卡,则视图可能无法正常工作。
  2. 如果使用多于5个选项卡,超出界限索引仅意味着该选项卡位于“更多”选项卡后面。在这种情况下,没有理由失败并使用assert,只需使用最后一个选项卡的框架即可。

所以我改变了他的代码一点点,我想出了这个:

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

工程就像一个魅力! – Tafkadasoh 2013-08-20 15:54:30

+15

鉴于UITabBarButton是一个私人iOS类,Apple的AppStore审查过程变得更加彻底,我认为谨慎的做法是根本不要任何对该类的引用 - 即使通过NSClassFromString()。但它是一个UIControl子类,所有UIControls都实现 - (CGRect)框架。所以你可以通过以下方式消除一堆条件: 'if([view isKindOfClass:[UIControl class]]) [tabBarItems addObject:view];' – 2013-09-16 22:32:29

+0

这似乎在旋转设备后返回不正确的结果。我从肖像旋转到风景,但它返回肖像值。当我从风景旋转到肖像时,它会返回风景值。我在'viewDidLayoutSubviews'中运行这段代码。 有什么想法? – BFeher 2014-10-06 08:04:12

2

我将添加对我来说,我简单的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点。

+0

很棒的发现!这比黑客呃更少。 – brynbodayle 2014-07-09 21:09:26

+7

它不适用于iPad,因为每个项目的宽度不等于tabbar分隔项目数量的宽度 – ZYiOS 2014-08-22 10:50:08

4

选项卡栏按钮是启用了用户交互的唯一子视图。检查此而不是UITabBarButton,以避免违反隐藏的API。

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

一旦在数组中,根据原点x对它进行排序,并且您将有按其真实顺序的标签栏按钮。

6

另一种可能,但草率的解决办法是以下几点:

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

在雨燕4.0:

private func frameForTabAtIndex(index: Int) -> CGRect { 
    var frames = view.subviews.flatMap { (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 
} 
+0

这对iPhone很好,但似乎在iPad上工作不正确。在iPad上,项目(或至少 - 图像)放置在中心,同时返回等宽的帧。 – KlimczakM 2016-10-10 10:36:12

+0

@KlimczakM这应该返回实际项目的框架 - 如果你知道图像大小,你可以在'flatMap'调用中创建CGRects,给定大小,以'view.center'为中心? – buildsucceeded 2016-10-18 12:44:18

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;

0

斯威夫特+ 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) 
}