Подтвердить что ты не робот

Получение рамки определенного элемента панели вкладок

Есть ли способ найти кадр конкретного UITabBarItem в UITabBar?

В частности, я хочу создать анимацию изображения, "падающего" на одну из вкладок, аналогичную, например, удаление электронной почты в Mail или покупка трека в приложении iTunes. Поэтому мне нужны координаты цели для анимации.

Насколько я могу судить, нет никакого открытого API для получения координат, но хотелось бы ошибаться в этом. Короче, мне придется оценивать координаты, используя индекс данной вкладки относительно рамки рамки табуляции.

4b9b3361

Ответ 1

Внедрение Imre содержит пару важных деталей imho.

  • Представления UITabBarButton необязательно в порядке. Например, если у вас более 5 вкладок на iPhone и перераспределенные вкладки, просмотры могут быть не в порядке.
  • Если вы используете более 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;
}

Ответ 2

Подразделы, связанные с элементами панели вкладок в 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;
}

Следует отметить, что структура (subviews) UITabBar и сам класс UITabBarButton не являются частью публичного API, поэтому теоретически он может изменяться в любой новой версии iOS без предварительного уведомления. Тем не менее маловероятно, что они изменят такие детали, и он отлично работает с iOS 5-6 и предыдущими версиями.

Ответ 3

Другим возможным, но неаккуратным решением будет следующее:

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

Ответ 4

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

Ответ 5

Расширяя 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
    }

}

Ответ 6

Кнопки панели вкладок являются единственными подменю, в которых включено взаимодействие с пользователем. Проверьте это вместо UITabBarButton, чтобы избежать нарушения скрытых API.

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

Как только в массиве, отсортируйте его на основе начала x, и у вас будут кнопки панели вкладок в их истинном порядке.

Ответ 7

Я добавлю, что сработало для меня в моем простом сценарии 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 ];
}

Что он делает, так это вычисление rect в UITabBar, где находится кнопка, находит центр этого прямоугольника и выкапывает представление в этой точке через hitTest: withEvent.

Ответ 8

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

Ответ 9

Вам не нужен какой-либо частный 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;