UIBarButtonItem:如何找到其框架?


Answers:


94

试试这个;

UIBarButtonItem *item = ... ;
UIView *view = [item valueForKey:@"view"];
CGFloat width;
if(view){
    width=[view frame].size.width;
}
else{
    width=(CGFloat)0.0 ;
}

6
没有“ Appstore安全”的定义。您的程序没有崩溃,没有黑客,没有使苹果不想要的东西无效,然后它就没事了。很多时候,由于奇怪的原因,应用被拒绝了。
Anoop Vaidya

3
好的,我之前提到[item valueForKey:@“ view”]使用的是未记录的API方法...
极权主义者

1
并且也请发布您的答案。这样搜索的人将找不到很少的答案,而会寻求更好的答案。
Anoop Vaidya

27
“没有Appstore安全的定义”。该声明无效。使用私有API并非“ Appstore安全”。期间
aryaxt 2015年

1
@LearneriOS:您编写的代码越多,您就会学到更多。一旦开始潜水,您将了解obj-c运行时以及wwdc和其他旧代码...等等将帮助您学习更多记载的内容。
Anoop Vaidya

20

这种方式最适合我:

UIView *targetView = (UIView *)[yourBarButton performSelector:@selector(view)];
CGRect rect = targetView.frame;

我非常喜欢您的解决方案,因为它很简单,并且直接基于按钮的标识符。它运作良好,但是看起来它的框架比预期的要大。我使用它来计算的位置PopOver,并将其放置在按钮下方的较低位置。这是否与您的解决方案有关,如果有,您是否有(其他)解决方案?提前致谢!
Tum 2014年

@Tumtum听起来像是如何添加弹出窗口的问题。将弹出窗口添加到self.navigationController.view而不是self.view
MobileMon

@转动该角度,或者只是从CGRect的y原点减去一些像素:rect.origin.y = rect.origin.y-5;
MobileMon 2014年

你是救星!我使用了两种解决方案的组合,因为仅使用您给我的第一个选项将弹出窗口定位得有点高,所以我使用了第二个选项进行了更正。感谢您抽出宝贵时间回答我的子问题。
Tum 2014年

13

感谢Anoop Vaidya的建议答案。一种替代方法是(假设您知道按钮在工具栏中的位置)

UIView *view= (UIView *)[self.toolbar.subviews objectAtIndex:0]; // 0 for the first item


CGRect viewframe = view.frame;

这是个好方法,您是否无法通过循环所有视图并检查一些标签或插座名称来找到索引?
Anoop Vaidya

谢谢。关于循环的好主意,但就我的情况而言,我知道索引,因此不需要循环。
极权主义者

在这种情况下...您不知道索引...那么您可以使用循环找到它。这将是所有人的通用代码。
Anoop Vaidya 2013年

还请记住,它subviews可能为空,在这种情况下,代码将导致崩溃。因此,在运输应用程序中,您应该检查一下。

1
对于iOS 6,子视图的顺序会发生变化,对吧?现在,第一个按钮是最后一个按钮,反之亦然
aprunedamtz

13

使用Swift时,如果需要经常使用条形按钮项目,则应实现如下扩展:

extension UIBarButtonItem {

    var frame: CGRect? {
        guard let view = self.value(forKey: "view") as? UIView else {
            return nil
        }
        return view.frame
    }

}

然后,您可以在代码中轻松访问:

if let frame = self.navigationItem.rightBarButtonItems?.first?.frame {
    // do whatever with frame            
}

@PoolHallJunkie for iOS11尝试在ViewDidAppear()方法中调用此代码
Hosny

这不是正确的解决方案,因为它使用私有API。您应该子类化以提供不会失败的解决方案。
PoolHallJunkie

11

哎呀,这个线程有很多粗略的答案。这是正确的方法:

import UIKit

class ViewController: UIViewController {

    let customButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()

        customButton.setImage(UIImage(named: "myImage"), for: .normal)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: customButton)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print(self.customButton.convert(self.customButton.frame, to: nil))
    }
}

1
即使此示例使用导航项,也应适用于工具栏。只要确保当前已显示视图控制器并显示工具栏即可。
Dan Stenmark

这在iOS 11,iOS 9,iOS 10上运行良好。感谢您的回答!
拉尼斯

当然,这仅适用于具有自定义视图的UIBarButtonItems。
fishinear

4

这是我在iOS 11和Swift 4中使用的内容。如果没有可选的选项,可能会更干净一些,但我正在安全地使用它:

extension UIBarButtonItem {
    var view: UIView? {
        return perform(#selector(getter: UIViewController.view)).takeRetainedValue() as? UIView
    }
}

和用法:

if let barButtonFrame = myBarButtonItem.view?.frame {
    // etc...
}

3
-(CGRect) getBarItemRc :(UIBarButtonItem *)item{
    UIView *view = [item valueForKey:@"view"];
    return [view frame];
}

根据第一个答案,不安全的应用商店。
fatuhoku 2015年

1

您可以通过使用navigationBar之类的属性layoutMarginsframe与《人机界面指南》中的图标尺寸指南相结合来大致计算它,并考虑当前设备的方向:

- (CGRect)rightBarButtonFrame {
    CGFloat imageWidth = 28.0;
    CGFloat imageHeight = UIDevice.currentDevice.orientation == UIDeviceOrientationLandscapeLeft || UIDevice.currentDevice.orientation == UIDeviceOrientationLandscapeRight ? 18.0 : 28.0;
    UIEdgeInsets navigationBarLayoutMargins = self.navigationController.navigationBar.layoutMargins;
    CGRect navigationBarFrame = self.navigationController.navigationBar.frame;
    return CGRectMake(navigationBarFrame.size.width-(navigationBarLayoutMargins.right + imageWidth), navigationBarFrame.origin.y + navigationBarLayoutMargins.top, imageWidth, imageHeight);
}

0

试试这个实现:

@implementation UIBarButtonItem(Extras)

- (CGRect)frameInView:(UIView *)v {

    UIView *theView = self.customView;
    if (!theView.superview && [self respondsToSelector:@selector(view)]) {
        theView = [self performSelector:@selector(view)];
    }

    UIView *parentView = theView.superview;
    NSArray *subviews = parentView.subviews;

    NSUInteger indexOfView = [subviews indexOfObject:theView];
    NSUInteger subviewCount = subviews.count;

    if (subviewCount > 0 && indexOfView != NSNotFound) {
        UIView *button = [parentView.subviews objectAtIndex:indexOfView];
        return [button convertRect:button.bounds toView:v];
    } else {
        return CGRectZero;
    }
}

@end

这具有与其他答案相同的缺点(需要定义未记录的选择器“视图”),并且在超级视图中进行完全无意义的查找。
fishinear

0

您应该在子视图上循环,并检查其类型或内容以进行识别。通过kvo访问视图并不安全,您不能确定索引。



0

您可以使用自定义视图(即UIButton)创建UIBarButtonItem,然后您可以执行任何操作。:]


0

在Swift 4.2中并受到luca的启发

extension UIBarButtonItem {

    var frame:CGRect?{
        return (value(forKey: "view") as? UIView)?.frame
    }

}


guard let frame = self.navigationItem.rightBarButtonItems?.first?.frame else{ return }

0

Swift 4 up当前最好的方法是从以下位置访问其框架:

self.navigationItem.rightBarButtonItems 通过

let customView = navigationItem.rightBarButtonItems?.first?.customView // access the first added customView

这种访问方式比访问私有api安全。

  • 在此查看答案:

将CustomView添加到navigationItem之后,CustomView始终返回nil


仅当UIBarButtonItem基于customView时才有效(大多数不是)。
fishinear

0

我在条形按钮项上使用了一个视图,并在视图上使用了一个标签:

for view in bottomToolbar.subviews {
   if let stackView = view.subviews.filter({$0 is UIStackView}).first {
       //target view has tag = 88
       if let targetView = stackView.subviews.filter({$0.viewWithTag(88) != nil}).first {                                
          //do something with target view
   }
}

}


-1

这是这样做的方法:

-(CGRect)findFrameOfBarButtonItem{
    for (UIView *view in self.navigationController.navigationBar.subviews)
    {
       if ([view isKindOfClass:NSClassFromString(@"_UINavigationBarContentView")])
       {
         UIView * barView = [self.navigationItem.rightBarButtonItem valueForKey:@"view"];
         CGRect barFrame = barView.frame;
         CGRect viewFrame = [barView convertRect:barFrame toView:view];
         return viewFrame;
       }
    }

    return CGRectZero;
}

2
不,这不对。您不应将KVC用于无法控制的代码。因此,请不要将此黑客标记为“正确的方式”。
turingtest

这做出了关于UINavigationBar内部工作的各种假设,这些假设甚至不确定其他答案使用的“视图”假设。
fishinear
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.