UIBarButtonItem: How can I find its frame?
IosObjective CSwiftIos Problem Overview
I have a button in a toolbar. How can I grab its frame? Do UIBarButtonItem
s not have a frame
property?
Ios Solutions
Solution 1 - Ios
Try this one;
UIBarButtonItem *item = ... ;
UIView *view = [item valueForKey:@"view"];
CGFloat width;
if(view){
width=[view frame].size.width;
}
else{
width=(CGFloat)0.0 ;
}
Solution 2 - Ios
This way works best for me:
UIView *targetView = (UIView *)[yourBarButton performSelector:@selector(view)];
CGRect rect = targetView.frame;
Solution 3 - Ios
With Swift, if you needs to often work with bar button items, you should implement an extension like this:
extension UIBarButtonItem {
var frame: CGRect? {
guard let view = self.value(forKey: "view") as? UIView else {
return nil
}
return view.frame
}
}
Then in your code you can access easily:
if let frame = self.navigationItem.rightBarButtonItems?.first?.frame {
// do whatever with frame
}
Solution 4 - Ios
Oof, lots of rough answers in this thread. Here's the right way to do it:
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))
}
}
Solution 5 - Ios
Thanks to Anoop Vaidya for the suggested answer. An alternative could be (providing you know the position of the button in the toolbar)
UIView *view= (UIView *)[self.toolbar.subviews objectAtIndex:0]; // 0 for the first item
CGRect viewframe = view.frame;
Solution 6 - Ios
Here's what I'm using in iOS 11 & Swift 4. It could be a little cleaner without the optional but I'm playing it safe:
extension UIBarButtonItem {
var view: UIView? {
return perform(#selector(getter: UIViewController.view)).takeRetainedValue() as? UIView
}
}
And usage:
if let barButtonFrame = myBarButtonItem.view?.frame {
// etc...
}
Edit: I don't recommend using this anymore. I ended up changing my implementation to use UIBarButtonItems with custom views, like Dan's answer
Solution 7 - Ios
-(CGRect) getBarItemRc :(UIBarButtonItem *)item{
UIView *view = [item valueForKey:@"view"];
return [view frame];
}
Solution 8 - Ios
You can create a UIBarButtonItem with a custom view, which is a UIButton, then you can do whatever you want. :]
Solution 9 - Ios
You can roughly calculate it by using properties like layoutMargins
and frame
on the navigationBar, combined with icon size guides from Human Interface Guidelines and take into count the current device orientation:
- (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);
}
Solution 10 - Ios
Try this implementation:
@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
Solution 11 - Ios
You should do a loop over the subviews and check their type or their contents for identifying. It is not safe to access view by kvo and you cannot be sure about the index.
Solution 12 - Ios
Check out this answer: https://stackoverflow.com/questions/35611106/how-to-apply-borders-and-corner-radius-to-uibarbuttonitem which explains how to loop over subviews to find the frame of a button.
Solution 13 - Ios
in Swift 4.2 and inspired with luca
extension UIBarButtonItem {
var frame:CGRect?{
return (value(forKey: "view") as? UIView)?.frame
}
}
guard let frame = self.navigationItem.rightBarButtonItems?.first?.frame else{ return }
Solution 14 - Ios
I used a view on the bar button item with a tag on the view:
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
}
}
}
Solution 15 - Ios
Swift 4 up The current best way to do it is to access its frame from :
self.navigationItem.rightBarButtonItems
by
let customView = navigationItem.rightBarButtonItems?.first?.customView // access the first added customView
Accessing this way is safer than accessing private api.
- check out the answer in this :