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

有没有办法在UITabBarItem中find特定的UITabBarItem的框架?

具体来说,我想创build一个“落入”选项卡的图像的animation,类似于例如删除邮件中的电子邮件,或者购买iTunes应用程序中的曲目。 所以我需要animation的目标坐标。

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

伊姆雷的实施缺less一些重要的细节。

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

与UITabBar中的标签栏项目相关联的子视图是UITabBarButton类的。 通过使用两个选项卡loggingUITabBar的子视图:

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

基于这个解决scheme是微不足道的。 我写这个的方法如下:

 + (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版本中进行更改,而无需事先通知。 尽pipe如此,他们不太可能会改变这种细节,并且在iOS5-6以及之前的版本中可以正常工作。

另一个可能的,但马虎的解决scheme将是以下内容:

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

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

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

一旦在数组中,根据原点x对它进行sorting,并且您将具有按其正确顺序的标签栏button。

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

我将在我的简单的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中button所在位置的矩形,find该矩形的中心,并通过hitTest:withEvent在该点挖出视图。

您不需要任何私人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;

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