调整 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,真正的标题视图是MDLMessagesNavigationTitleViewMDLMessagesNavigationTitleView是由自动布局管理的,因为它的超级视图是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 的框架?的主要内容,如果未能解决你的问题,请参考以下文章

在窗口调整大小时动态调整画布大小

在窗口调整大小时调整图像地图大小

excel小技巧之快速调整行高列宽 如何快速调整行高列宽

调整事项与非调整事项

调整大小后调整 UIPopoverController 位置

starUML如何调整用例大小