UIBarButtonItem:我怎样才能找到它的框架?

Posted

技术标签:

【中文标题】UIBarButtonItem:我怎样才能找到它的框架?【英文标题】:UIBarButtonItem: How can I find its frame? 【发布时间】:2013-01-14 12:25:39 【问题描述】:

我在工具栏中有一个按钮。我怎样才能抓住它的框架? UIBarButtonItems 没有 frame 属性吗?

【问题讨论】:

那是因为UIBarButtonItems 不是UIViews。为什么需要框架?也许有更好的方法 基本上我正在使用 github.com/Ciechan/BCGenieEffect#bcgenieeffect 并尝试模拟视图“吸入”我工具栏上的按钮 【参考方案1】:

试试这个;

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

else
    width=(CGFloat)0.0 ;

【讨论】:

没有“Appstore安全”的定义。你的程序没有崩溃,你没有黑客攻击,你没有使苹果不想要的任何东西失效,那么它就可以了。很多时候应用会因为奇怪的原因被拒绝。 好的,我看到之前提到过 [item valueForKey:@"view"] 正在使用未记录的 API 方法... 也请发布您的答案。这样搜索的人会发现很少的答案并寻求更好的答案。 “Appstore安全没有定义”。该声明无效。使用私有 API 不是“Appstore 安全的”。期间 @Learnerios:你编码的越多,你学到的就越多。一旦开始潜水,您就会了解 obj-c 运行时以及 wwdc 和其他遗留代码...等将帮助您了解更多未记录的东西。【参考方案2】:

这种方式最适合我:

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

【讨论】:

我真的很喜欢你的解决方案,因为它很简单并且直接基于按钮的标识符。它运作良好,但它的框架似乎比预期的要大。我用它来计算PopOver 的位置,它被放置在按钮下方一点点的位置。这是否与您的解决方案有关,如果是,您是否有(其他)解决方案?提前致谢! @Tumtum 听起来像是如何添加弹出框的问题。将弹出框添加到 self.navigationController.view 而不是 self.view @Turnturn 或者只是从 CGRect 的 y 原点减去一些像素:rect.origin.y = rect.origin.y - 5; 你是救生员!我使用了您的两种解决方案的组合,因为仅使用您给我的第一个选项,弹出框的位置有点太高了,所以我使用第二个选项更正了。感谢您花时间回答我的子问题。【参考方案3】:

使用 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() 方法中调用此代码 这不是一个合适的解决方案,因为它使用私有 API。您应该子类化以获得不会失败的解决方案。【参考方案4】:

哎呀,这个帖子中有很多粗略的答案。以下是正确的做法:

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

【讨论】:

即使这个例子使用了一个导航项,同样适用于工具栏。只需确保当前显示视图控制器并显示工具栏。 这适用于 iOS 11、iOS 9、iOS 10。感谢您的回答! 当然,这只适用于具有自定义视图的 UIBarButtonItems。【参考方案5】:

感谢 Anoop Vaidya 提供建议的答案。另一种方法可能是(假设您知道工具栏中按钮的位置)

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


CGRect viewframe = view.frame;

【讨论】:

这是个好方法,你不能通过循环所有视图并检查一些标签或插座名称来找到索引吗? 谢谢。关于循环的好主意,但对于我的情况,我知道索引,因此不需要循环。 在这种情况下......如果你不知道索引......那么你可以使用循环找到它。这将是所有人的通用代码。 还要记住subviews 可能为空,在这种情况下代码会导致崩溃。因此,在运输应用程序中,您应该检查这一点。 iOS 6 的子视图顺序发生了变化,对吧?第一个按钮现在是最后一个按钮,反之亦然【参考方案6】:

这是我在 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...

编辑:我不建议再使用它了。我最终更改了我的实现以使用带有自定义视图的 UIBarButtonItems,例如 Dan's answer

【讨论】:

我真的很喜欢这个答案,并在生产环境中使用它。可悲的是,它导致 objc_retain 偶尔崩溃。 也许使用 takeUnretainedValue()?这里发生内存泄漏的风险是否大于崩溃的风险? @RonRegev 查看编辑。我最终使用了自定义视图。这是更多的工作,但最终是一个更好、更安全的解决方案。祝你好运!【参考方案7】:
-(CGRect) getBarItemRc :(UIBarButtonItem *)item
    UIView *view = [item valueForKey:@"view"];
    return [view frame];

【讨论】:

不是应用商店安全的,根据第一个答案。【参考方案8】:

你可以创建一个带有自定义视图的 UIBarButtonItem,它是一个 UIButton,然后你可以做任何你想做的事情。 :]

【讨论】:

【参考方案9】:

您可以通过使用导航栏上的layoutMarginsframe 等属性,结合Human Interface Guidelines 中的图标大小指南进行粗略计算,并考虑当前设备方向:

- (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);

【讨论】:

【参考方案10】:

试试这个实现:

@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

【讨论】:

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

您应该对子视图进行循环并检查它们的类型或内容以进行识别。 通过 kvo 访问视图是不安全的,您无法确定索引。

【讨论】:

【参考方案12】:

查看这个答案:How to apply borders and corner radius to UIBarButtonItem?,它解释了如何遍历子视图以查找按钮的框架。

【讨论】:

【参考方案13】:

在 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 

【讨论】:

【参考方案14】:

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

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
   

【讨论】:

【参考方案15】:

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

self.navigationItem.rightBarButtonItemsby

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

这样访问比访问私有api更安全。

在此查看答案:

After Add a CustomView to navigationItem, CustomView always return nil

【讨论】:

这仅在 UIBarButtonItem 基于 customView 时才有效(大多数都不是)。

以上是关于UIBarButtonItem:我怎样才能找到它的框架?的主要内容,如果未能解决你的问题,请参考以下文章

我怎样才能找到空间的角色?

UITests:如何通过带有谓词的accessibilityIdentifier 找到UIBarButtonItem?

我怎样才能找到部分单词匹配/找到c ++

AS3 - 我怎样才能找到下载前 mp3 的总时间?

使用 PhoneGap 在 UIToolBar 中定位 UIBarButtonItem

UIToolBar 具有降低的 alpha,希望 UIBarButtonItem 具有 alpha 1