调整 navigationItem.titleView 的框架?
Posted
技术标签:
【中文标题】调整 navigationItem.titleView 的框架?【英文标题】:Adjusting navigationItem.titleView's frame? 【发布时间】:2010-09-10 02:52:57 【问题描述】:我注意到titleView 的位置取决于rightbarbutton 的标题。如何将 titleView 的框架设置为居中?
我试过设置titleView.frame,但是没有用。
代码如下:
UIButton *titleLabel = [UIButton buttonWithType:UIButtonTypeCustom];
[titleLabel setTitle:@"Right Eye" forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
titleLabel.frame = CGRectMake(100, 0, 180, 44);
titleLabel.titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.titleLabel.font = [UIFont boldSystemFontOfSize:20.0];
titleLabel.titleLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.3];
// titleLabel.titleLabel.textAlignment = UITextAlignmentCenter; (this doesn't work either)
[titleLabel addTarget:self action:@selector(titleTap:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.titleView = titleLabel;
self.navigationItem.titleView.frame = CGRectMake(100, 0, 180, 44); //(this doesn't work either)
【问题讨论】:
我也遇到了同样的问题!左侧或右侧的按钮导致 titleView 居中,即使我在 layoutSubviews 中完全设置它 【参考方案1】:有时在您的视图控制器的 viewWillAppear: 和 viewDidAppear: 之间,导航栏会重新定位您的 titleView 并在必要时调整其大小以适应。它最终可能会不居中,因为导航栏不希望调整 titleView 的大小以适应按钮之间的大小。如果标题视图足够小,它似乎会尝试将其居中,但如果不是,则会将您的 titleView 移出中心,然后我认为只有作为最后的手段才会调整大小。效果是 titleView 占据了两个按钮之间的所有空间,如果它的 textAlignment 居中,那么它将在 那个 空间中居中,但不是相对于整个屏幕居中。您可以在屏幕截图中看到这一点。
一个答案是让你的titleView更窄,以便导航栏可以居中,所以当你设置你的titleView.frame时尝试大约160而不是180。糟糕的是,当像您一样以编程方式创建视图时,必须在代码中对该宽度进行估计。最好的方法是在保持居中的同时最大化该空间。
可以一开始titleView是屏幕宽度,然后导航栏改变titleView.frame后,在view controller的viewDidAppear:中自己再更新一次。使用其调整后的 titleView.frame.origin.x 和 size.width 以及屏幕宽度,您可以计算出左右边距的最大值,然后将 origin.x 设置为该值,将 size.width 设置为屏幕宽度减去那个时间是 2。但是,直到推送的视图动画化并且之后的转变可见之后才会生效。您可以将 titleView 隐藏在 viewWillAppear: 中,然后在 viewDidAppear: 中取消隐藏,然后将其居中,这样就不会使用偏离中心的 titleView 滑入,然后移动它,它会滑入而不会出现任何标题。
更好的方法是让您的 titleView 成为您自己的 UIView 子类(或 UILabel 或其他)并覆盖 setFrame 以调整自身大小以保持在其父视图的中心。如果我最终自己这样做,我会在这里发布。或者也许其他人知道另一个地方可以在 titleView 的框架被更新之后但在没有视图子类的视图滑入之前更改它。
【讨论】:
这确实应该被标记为答案。使用它,我使视图的宽度完全符合标签中的文本所需的宽度。效果很好! 感谢您写下这个答案,它对我帮助很大。 我对 navigationBar 的 frame 属性进行了观察,并在 navigationBar 的 frame 发生变化时按照您的建议进行了操作。就我而言,问题发生在方向变化上。【参考方案2】:设置完你的titleView或titleLabel后,调用sizeToFit
就可以了,还要确保titleLabel.textAlignment = UITextAlignmentCenter
。它将以导航栏的宽度为中心,而不是在按钮边缘和导航栏的远边缘之间的空间中。
【讨论】:
这很简单。当我第一次阅读答案时,我已经阅读了长篇“不可能”的答案并且有点害怕。这应该被标记为正确的。 这很好用。它也适用于其他 UIView,例如 UIButton。【参考方案3】:这里的建议都没有真正对我有用。我的问题是我在导航栏处于过渡中间时设置了 titleView - 我会得到这个奇怪的抖动,titleView 会向左闪烁,然后又回到中心。
我最终遵循了 smallduck 的想法并覆盖了 setFrame,就像这样简单:
- (void)setFrame:(CGRect)frame
[super setFrame:CGRectMake(0, 0, self.superview.bounds.size.width, self.superview.bounds.size.height)];
【讨论】:
【参考方案4】:您可以尝试最初将框架设置为 CGRectZero 并调用 sizeToFit。下面是我用来更改标题文本并确保它仍然居中的代码。我在左边有一个后退按钮。
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = @"Title";
[label sizeToFit];
self.navigationItem.titleView = label;
【讨论】:
而不是将 frame 设置为 CGRectZero 你可以做 ` UILabel *label = [[UILabel alloc] init];`【参考方案5】:在您的自定义视图中覆盖 intrinsicContentSize
:
- (CGSize )intrinsicContentSize
return CGSizeMake(180, 44);
【讨论】:
【参考方案6】:-(void) awakeFromNib
UIButton *titleLabel = [UIButton buttonWithType:UIButtonTypeCustom];
[titleLabel setTitle:@"Right Eye" forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[titleLabel setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
titleLabel.titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.titleLabel.font = [UIFont boldSystemFontOfSize:20.0];
titleLabel.titleLabel.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.3];
[titleLabel addTarget:self action:@selector(titleTap:) forControlEvents:UIControlEventTouchUpInside];
titleLabel.frame = CGRectMake(0, 0, 400, 20);
self.navigationItem.titleView = titleLabel;
【讨论】:
【参考方案7】:我的解决方案是在 viewDidAppear 中手动调整导航栏的大小:
- (void)viewDidAppear
[super viewDidAppear];
[self.navigationController.navigationBar setFrame:CGRectMake(x, y, width, height)];
...之后您也可以重新定位标题视图。我刚刚注意到主要问题是导航栏也调整了大小,所以我把它恢复到正常大小。我从调试中得到了常规大小,并在修改之前在 viewDidLoad 中检查了它的大小......
【讨论】:
这是最适合我的解决方案。我必须根据按钮图像执行 switch 语句,这可能会根据用户选择的内容而有所不同。我从调试器中获取了有关大小的信息并进行了相应的调整。【参考方案8】:同样的问题。将自动滚动标签实现为 titleView。这样导航标题中的长文本可以自动滚动以供用户查看。 在最简单的特殊情况下,titleView 的未知实际尺寸的所有问题都会阻止文本居中:当标签文本适合且不需要自动滚动时(例如将文本设置为 navigationItem 的标题属性)。
【讨论】:
【参考方案9】:我还有一个稍微不同的用例(在 ios 7 上 UIButton 作为 titleView 的垂直偏移)。很长一段时间后,我想出了使用 UIButton 的 contentEdgeInsets
属性来移动其框架中的内容。
【讨论】:
【参考方案10】:我发现解决此问题的唯一方法是不使用 UINavigationItems。 Insted 在 UINavigationBar 的 eace 按钮侧使用两个不同的视图,如下所示:
-(UIView *)rightButtonViewSection
if (!_rightButtonViewSection)
_rightButtonViewSection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];
return _rightButtonViewSection;
-(UIView *)leftButtonViewSection
if (!_leftButtonViewSection)
_leftButtonViewSection = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 45)];
return _leftButtonViewSection;
然后像这样设置按钮:
ButtonBase *searchButton = [[ButtonBase alloc] initWithFrame:CGRectMake(0, 0, 32, 30)];
[searchButton setImage:[UIImage imageNamed:SEARCH_IMAGE] forState:UIControlStateNormal];
SEL searchSelector = @selector(tradersSearchButtonPress);
[searchButton addTarget:self action:searchSelector forControlEvents:UIControlEventTouchUpInside];
self.notificationButton.view.frame = [self rightButtonFrame];
searchButton.frame = [self rightSecoundButtonFrame];
[self.rightButtonViewSection removeAllSubviews];
[self.rightButtonViewSection addSubview:self.notificationButton.view];
[self.rightButtonViewSection addSubview:searchButton];
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:self.rightButtonViewSection];
self.topViewController.navigationItem.rightBarButtonItem = buttonItem;
按钮框架:
-(CGRect) rightButtonFrame
return CGRectMake(self.rightButtonViewSection.width - 32, 7, 32, 30);
-(CGRect) rightSecoundButtonFrame
return CGRectMake(self.rightButtonViewSection.width - 80, 7, 32, 30);
那么标题就不动了!因为 UIButtonsView 的宽度相同。
【讨论】:
【参考方案11】:我正在实现一个两行标题,类似于 WhatsApp 正在做的事情(标题和副标题)。
我通过子类化UINavigationController
并覆盖viewDidLayoutSubviews()
解决了这个问题。
我的 titleView 如下所示(在 viewController
中):
let frame = self.navigationController?.navigationBar.bounds ?? CGRect.zero
// This is a container view for the two labels, laying them out vertically
let titleView = UIStackView(arrangedSubviews: [self._titleLabel, self._promptLabel])
titleView.axis = .vertical
titleView.alignment = .center
titleView.distribution = .fill
titleView.frame = frame // set the frame to the navbarFrame initially
titleView.spacing = 0
// The wrapper is necessary for laying out the stackView as the titleView
let wrapperView = UIView(frame: frame)
wrapperView.addSubview(titleView)
self.navigationItem.titleView = wrapperView
我的viewDidLayoutSubviews()
:
guard let navigationItem = self.topViewController?.navigationItem,
let titleView = navigationItem.titleView,
let wrapperView = titleView.subviews[0] as? UIStackView else return
var width : CGFloat = 0
for view in wrapperView.arrangedSubviews
view.sizeToFit()
width = max(width, view.frame.size.width)
titleView.frame.size.width = width
titleView.frame.size.height = self.navigationBar.frame.height
wrapperView.frame = titleView.bounds
wrapperView.center.x = self.navigationBar.convert(self.navigationBar.center, to: titleView).x
wrapperView.center.y = titleView.frame.height / 2
self.navigationBar.layoutIfNeeded()
注意: 诀窍是在 titleView 周围有一个包装视图,可以通过导航栏进行布局。然后将您的 titleView 的框架调整为您想要的。
【讨论】:
【参考方案12】:最近我遇到了同样的问题,我有一个复杂的标题视图,其内容可能会发生变化,所以我希望可以显示尽可能多的内容,我的左栏按钮项目和右栏按钮项目的宽度不同,所以如果我将其设置为长宽度,导航栏不会居中。你可能知道,当左或右栏按钮项很长时,不可能将标题视图居中,所以我想尽可能地居中。
一旦我们将视图设置为self.navigationItem.titleView
,导航栏将管理标题视图的框架,很难直接将其居中,所以我将一个视图子类化为真正的标题视图的容器视图,我将其宽度设置为屏幕宽度,UINavigationBar
,将其框架设置为与其左/右栏按钮项相关的属性值。
容器视图是MDLNavigationTitleContainer
,真正的标题视图是MDLMessagesNavigationTitleView
,MDLMessagesNavigationTitleView
是由自动布局管理的,因为它的超级视图是MDLNavigationTitleContainer
,每次宽度改变时,layoutSubviews
都会被调用. layoutSubviews
中的代码看起来有点怪,是用来处理push/pop动画的。
@interface MDLNavigationTitleContainer ()
@property (nonatomic) CGRect oldFrame;
@end
@implementation MDLNavigationTitleContainer
- (instancetype)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
UINib *nib = [UINib nibWithNibName:@"MDLMessagesNavigationTitleView" bundle:[NSBundle bundleForClass:[MDLMessagesNavigationTitleView class]]];
self.titleView = [[nib instantiateWithOwner:nil options:nil] firstObject];
self.titleView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:self.titleView];
NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0];
width.priority = 200;
NSLayoutConstraint *width1 = [NSLayoutConstraint constraintWithItem:self.titleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
[self addConstraint:centerX];
[self addConstraint:centerY];
[self addConstraint:width];
[self addConstraint:width1];
return self;
- (void)layoutSubviews
[super layoutSubviews];
if (!self.superview)
return;
if (self.center.x > [UIScreen mainScreen].bounds.size.width)
self.titleView.frame = self.oldFrame;
return;
CGFloat centerX = self.center.x;
CGFloat superWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat superCenterX = superWidth/2;
CGFloat offsetX = superCenterX - centerX;
CGPoint center = self.titleView.center;
if (CGRectGetMaxX(self.titleView.frame) + offsetX < self.bounds.size.width &&
CGRectGetMinX(self.titleView.frame) + offsetX > 0)
center = CGPointMake(self.bounds.size.width/2 + offsetX, self.bounds.size.height/2);
CGSize size = self.titleView.bounds.size;
if (size.width > self.bounds.size.width)
size.width = self.bounds.size.width;
self.titleView.frame = CGRectMake(center.x-size.width/2, center.y-size.height/2, size.width, size.height);
self.oldFrame = self.titleView.frame;
设置标题视图:
MDLNavigationTitleContainer *titleView = [[MDLNavigationTitleContainer alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44)];
self.navigationItem.titleView = titleView;
【讨论】:
【参考方案13】:只需在导航栏右侧添加隐形按钮(无文本),并将其大小更改为例如 20 px
【讨论】:
以上是关于调整 navigationItem.titleView 的框架?的主要内容,如果未能解决你的问题,请参考以下文章