有没有办法找到UITabBar
中特定的UITabBarItem
框架?
具体而言,我想创建一个图像“落入”其中一个选项卡的动画,类似于例如在邮件中删除电子邮件或在iTunes应用程序中购买曲目。因此,我需要动画的目标坐标。
据我所知,没有公共API可以获取这些坐标,但很乐意接受错误的信息。如果没有这样的API,我将根据给定选项卡相对于选项卡栏框架的索引来猜测坐标。
有没有办法找到UITabBar
中特定的UITabBarItem
框架?
具体而言,我想创建一个图像“落入”其中一个选项卡的动画,类似于例如在邮件中删除电子邮件或在iTunes应用程序中购买曲目。因此,我需要动画的目标坐标。
据我所知,没有公共API可以获取这些坐标,但很乐意接受错误的信息。如果没有这样的API,我将根据给定选项卡相对于选项卡栏框架的索引来猜测坐标。
我认为Imre的实现缺少一些重要的细节。
因此,我稍微修改了他的代码,并得出了以下内容:
+ (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;
}
if ([view isKindOfClass:[UIControl class]])
[tabBarItems addObject:view];
- Greg CombsviewDidLayoutSubviews
中运行此代码。有什么想法吗? - BFeher另一种可能但不够规范的解决方案如下:
guard let view = self.tabBarVC?.tabBar.items?[0].valueForKey("view") as? UIView else
{
return
}
let frame = view.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和之前的版本中运行良好。
UIControl
作为一种子视图类进行比较。 - rafalkittaextension 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
}
}
在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
}
flatMap
调用内创建给定大小的CGRect,居中于view.center
? - buildsucceeded选项卡按钮是唯一启用了用户交互的子视图。为避免违反隐藏的API,请检查此选项而不是UITabBarButton。
for (UIView* view in self.tabBar.subviews)
{
if( [view isUserInteractionEnabled] )
{
[myMutableArray addObject:view];
}
}
将其放入数组后,基于原点x进行排序,您将按照真实顺序拥有选项卡按钮。
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)
}
在我简单的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 ];
}
Swift 5:
只需将以下代码放入处理选项卡的视图控制器中即可。
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;