TitleViewの位置が右バーボタンのタイトルに依存していることに気づきました。代わりに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)
ビューコントローラのviewWillAppear:とviewDidAppear:の間のいつか、ナビゲーションバーがtitleViewの位置を変更し、必要に応じてサイズを変更しています。ナビゲーションバーは、titleViewをボタンの間に収まるようにサイズ変更しないことを望んでいるため、結果的に中心から外れる可能性があります。十分に小さい場合はタイトルビューを中央に配置しようとするようですが、そうでない場合はtitleViewが中央からずれてしまい、最後の手段としてサイズが変更されるだけです。その効果は、titleViewが2つのボタンの間のすべてのスペースを占めていることです。textAlignmentが中央に配置されている場合、画面全体ではなく、thatスペースで中央に配置されます。これはスクリーンショットで確認できます。
1つの答えは、titleViewを狭くして、ナビゲーションバーが中央に配置できるようにすることです。titleView.frameを設定するときに、180ではなく160を試してください。あなたがそうであるようにプログラムでビューを作成するとき、人はその幅の見積もりをコードに入れなければならないことを吸い込みます。良いのは、中央にとどまりながらそのスペースを最大化する方法です。
TitleViewを画面の幅として開始することは可能ですが、ナビゲーションバーがtitleView.frameを変更した後、ビューコントローラーのviewDidAppear:で再度更新します。調整されたtitleView.frame.Origin.xとsize.widthを画面の幅とともに使用して、左と右のマージンの最大値を計算し、Origin.xをそれに設定し、size.widthを画面の幅からマイナスします。ただし、プッシュされたビューがアニメーション化され、その後のシフトが表示されるまでは有効になりません。 titleViewをviewWillAppear:で非表示にしてから、viewDidAppear:で再表示します。中央に配置した後、ずれたtitleViewでスライドしてシフトする代わりに、タイトルなしでスライドして表示されます。
より良い可能性は、titleViewをUIView(またはUILabelなど)の独自のサブクラスにして、setFrameをオーバーライドし、スーパービューの中央にとどまるようにサイズを変更することです。私がこれを自分でやろうと思ったら、ここに投稿します。あるいは、titleViewのフレームが更新された後、ビューがサブクラスなしでスライドインする前に、別の場所でtitleViewのフレームを変更する人がいるかもしれません。
TitleViewまたはtitleLabelを設定した後、それらに対してsizeToFit
を呼び出します。また、titleLabel.textAlignment = UITextAlignmentCenter
。ボタンエッジとナビゲーションバーの遠端の間のスペースではなく、ナビゲーションバーの幅の中央に配置されます。
ここでの提案はどれも、私にとってはうまくいきませんでした。私の問題は、ナビゲーションバーがトランジションの途中にあるときにtitleViewを設定していたことでした。titleViewが左にちらついて、中央に戻るという奇妙なジッタが発生しました。
私はスモールダックのアイデアに従い、setFrameをオーバーライドすることになりました。
- (void)setFrame:(CGRect)frame {
[super setFrame:CGRectMake(0, 0, self.superview.bounds.size.width, self.superview.bounds.size.height)];
}
最初にフレームをCGRectZeroに設定して、sizeToFitを呼び出すことができます。以下は、タイトルテキストを変更し、中央に配置するために使用したコードです。左に戻るボタンがありました。
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = @"Title";
[label sizeToFit];
self.navigationItem.titleView = label;
私の解決策は、viewDidAppearのナビゲーションバーのサイズを手動で変更することでした。
- (void)viewDidAppear
{
[super viewDidAppear];
[self.navigationController.navigationBar setFrame:CGRectMake(x, y, width, height)];
}
...その後、タイトルビューの位置も変更できます。ナビゲーションバーのwqaもサイズ変更されたことが主な問題であることに気付いたので、通常のサイズに戻しました。私は通常のサイズをデバッグから取得し、それが変更される前にviewDidLoadでそのサイズをチェックしています...
-(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;
}
Cunstomビューで_-intrinsicContentSize
_をオーバーライドする
- (CGSize )intrinsicContentSize { return CGSizeMake(180, 44); }
WhatsAppがやっていることに似た2行のタイトル(タイトルとサブタイトル)を実装していました。
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のフレームを希望どおりに調整します。
これを修正する唯一の方法は、UINavigationItemsを使用しないことです。以下のように、UINavigationBarのEaceボタン側で2つの異なるビューを使用します。
-(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;
}
そして、そのようにsetButtons:
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;
ButtonsFrames:
-(CGRect) rightButtonFrame {
return CGRectMake(self.rightButtonViewSection.width - 32, 7, 32, 30);
}
-(CGRect) rightSecoundButtonFrame {
return CGRectMake(self.rightButtonViewSection.width - 80, 7, 32, 30);
}
その後、タイトルは移動しません! UIButtonsViewsは同じ幅だからです。
ここでも同じ問題。自動スクロールラベルをtitleViewとして実装します。したがって、ナビゲーションタイトルの長いテキストを自動的にスクロールして、ユーザーがすべて表示できるようにすることができます。 titleViewの未知の実際の寸法に関するすべての問題は、最も単純な特別なケースでテキストの中央揃えを妨げます。
最近同じ問題が発生しました。内容が変更される可能性のある複雑なタイトルビューがあるため、できるだけ多くのコンテンツを表示したいので、左バーボタン項目と右バーボタン項目の幅が異なるため、設定すると幅が長い場合、ナビゲーションバーは中央に配置されません。左または右のバーボタンアイテムが非常に長い場合はご存知かもしれませんが、タイトルビューを中央に配置することは不可能なので、できるだけ中央に配置したいと思います。
ビューをself.navigationItem.titleView
に設定すると、ナビゲーションバーがタイトルビューのフレームを管理し、直接中央に配置するのが難しいため、実際のタイトルビューのコンテナービューとして機能するビューをサブクラス化し、画面の幅を設定します幅、UINavigationBar
を使用して、フレームを左/右バーボタン項目に関するプロパティ値に設定します。
コンテナビューはMDLNavigationTitleContainer
であり、実際のタイトルビューはMDLMessagesNavigationTitleView
です。MDLMessagesNavigationTitleView
は、スーパーレイアウトがMDLNavigationTitleContainer
であるため、自動レイアウトによって管理されます。幅が変更されると、layoutSubviews
が呼び出されます。 layoutSubviews
のコードは少し奇妙に見えますが、プッシュ/ポップアニメーションの処理に使用されます。
@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;
少し他の使用例があります(iOS 7のtitleViewとしてのUIButtonの垂直オフセット)。久しぶりに、UIButtonのcontentEdgeInsets
プロパティを使用して、フレーム内のコンテンツをシフトしました。