iOS 11 navigationItem.titleView 宽度未设置
iOS 11 navigationItem.titleView Width Not Set
在 iOS11 上看到一个 navigationItem.titleView 的行为,其中 titleView 的宽度不是屏幕的全宽。
我有一个自定义视图,我将其设置为 titleView。在 iOS11 之前,视图将填满导航栏区域。但是 iOS11 它没有调整大小以填充屏幕的宽度。
我试过在设置 titleView 之前设置视图的框架,但没有成功。我也试图强制 titleViews 超级视图布局约束,但没有成功。
附上截图:
iOS10:
iOS11:
还有其他人遇到过这种情况吗?
我明白了。我必须覆盖视图和文本字段的 intrinsicContentSize getter。
我将宽度设置为 CGFloat.greatestFiniteMagnitude,因此它始终与屏幕一样宽。
更新:
由于我已经在这个问题上花了几个小时,希望其他人能够通过将所有事情紧密结合起来更快地赶上来
我创建了 TitleView
的自定义子 class,名为 CustomTitleView
,代码如下:
import UIKit
class CustomTitleView: UIView {
override var intrinsicContentSize: CGSize {
return UIView.layoutFittingExpandedSize
}
}
我从一开始就错过的最重要的部分是:
将 intrinsicContentSize
设置为 UILayoutFittingExpandedSize
也可以正常工作
这里使用@falkon 的回答代码:
将此代码添加到用作 titleView 的视图中
override var intrinsicContentSize: CGSize {
return UILayoutFittingExpandedSize
}
我遇到了同样的问题,但是将 UIImage
设置为 navigationItem
titleView
我所做的是使用以下方法将图像缩放到所需的大小:
-(UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
并按如下方式调用:
-(void)setHeaderImage{
UIImage * image = [self imageWithImage:[UIImage imageNamed:@"headerImage"] scaledToSize:CGSizeMake(150, 27)];
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(0, 0, 150, 27);
imageView.contentMode = UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imageView;
}
最重要的是你需要覆盖 customTitleView 作为你的 titleView:
self.navigationItem.titleView = [self titleView];
#pragma mark - getter
- (UIView *)titleView {
UIView *navTitleView = [HFCalenderTitleView new];
navTitleView.frame = CGRectMake(0.0, 0.0, HorizontalFrom750(200.0), 44.0);
[navTitleView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(navTitleView);
}];
CGFloat btnWidth = 30.0;
[navTitleView addSubview:self.previousButton];
self.previousButton.imageEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 15.0);
[self.previousButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(navTitleView);
make.top.bottom.equalTo(navTitleView);
make.width.equalTo(@(btnWidth));
}];
[navTitleView addSubview:self.nextBtn];
self.nextBtn.imageEdgeInsets = UIEdgeInsetsMake(0.0, 15.0, 0.0, 0.0);
[self.nextBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(navTitleView);
make.top.bottom.equalTo(navTitleView);
make.width.equalTo(@(btnWidth));
}];
return navTitleView;
}
#pragma mark - customTitleView
#import "HFCalenderTitleView.h"
@implementation HFCalenderTitleView
- (CGSize)intrinsicContentSize{
return CGSizeMake(HorizontalFrom750(200.0), 40); // the target size
}
通过创建 UIView 的子类并将其分配给 UINavigationController 的标题视图来修复它
Objective-C:
#import "FLWCustomTitleView.h"
@implementation FLWCustomTitleView
- (CGSize )intrinsicContentSize {
return UILayoutFittingExpandedSize;
}
@end
如果您不想覆盖 intrinsicContentSize
,也可以使用约束。这是 SnapKit 的演示
self.navigationItem.titleView = titleView
if #available(iOS 11, *) {
titleView.snp.makeConstraints({ (make) in
make.width.equalTo(250) // fixed width
make.height.equalTo(30) // less than 44(height of naviagtion bar)
})
}else {
titleView.frame = ...
}
但是如果导航栏的任一侧(左侧或右侧)有多个导航栏项目,您应该使用 intrinsicContentSize;
当您在 CustomTitleView 中有一个 UIView 作为子视图时,intrinsicContentSize 解决方案不起作用,仅对 XCODE 9 和 iOS 11 中的我而言。所以我确实喜欢下面的内容,对我来说效果很好,这可能对某人有帮助。
@interface CustomTitleView : UIView
@property (weak, nonatomic) IBOutlet UIView *doubleTitleView;
@end
@implementation CustomTitleView
- (void)awakeFromNib {
[super awakeFromNib];
int width = _doubleTitleView.frame.size.width;
int height = _doubleTitleView.frame.size.height;
if (width != 0 && height != 0) {
NSLayoutConstraint *widthConstraint = [_doubleTitleView.widthAnchor constraintEqualToConstant:width];
NSLayoutConstraint *heightConstraint = [_doubleTitleView.heightAnchor constraintEqualToConstant:height];
[_doubleTitleView addConstraint:heightConstraint];
[_doubleTitleView addConstraint:widthConstraint];
[heightConstraint setActive:TRUE];
[widthConstraint setActive:TRUE];
}
}
return UILayoutFittingExpandedSize
对我没有帮助,因为视图被垂直添加了几次以填充布局。
解决方案是将自定义视图设置宽度中的 intrinsicContentSize
覆盖为最大屏幕宽度:
- (CGSize)intrinsicContentSize {
//fills empty space. View will be resized to be smaller, but if it is too small - then it stays too small
CGRect frame = self.frame;
frame.size.width = MAX(SCREEN_WIDTH, SCREEN_HEIGHT);
return frame.size;
}
我必须将 UIImageView 设置为 navigationItem.titleView。纵横比确实合适,但 intrinsicContentSize 使其变大了。
缩放图像导致图像质量差。
设置布局锚点对我有用:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 30)];
[imageView setImage:image];
[imageView.widthAnchor constraintEqualToConstant:80].active = YES;
[imageView.heightAnchor constraintEqualToConstant:30].active = YES;
[imageView setContentMode:UIViewContentModeScaleAspectFit];
self.navigationItem.titleView = imageView;
尝试使用标准 UISearchBar / UISearchController
实际上你需要做的 - 如果你可以使用标准的 UISearchBar / UISearchController 是按照以下方式显示搜索栏,它尊重安全区域,因此在 iPhone X 和每个设备方向:
func presentSearchController() {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.text = "any text"
if #available(iOS 11.0, *) {
self.navigationItem.searchController = searchController
searchController.isActive = true
} else {
present(searchController, animated: true, completion: nil)
}
}
参考资料
https://developer.apple.com/videos/play/fall2017/201/
https://medium.com/@PavelGnatyuk/large-title-and-search-in-ios-11-514d5e020cee
对现有答案的补充:
如果您的自定义标题视图是 默认情况下已经具有固有内容大小(.zero
除外)的视图,例如 UILabel
,一个UITextView
或一个UIButton
,你可以简单地设置
yourCustomTitleView.translatesAutoresizingMaskIntoConstraints = false
并且它会自动调整以仅包含其内容,但不会与左右项目视图重叠。
例如,您可以在 Interface Builder 中将一个按钮拖到导航栏的标题视图区域,在您的视图控制器中为其创建一个插座 titleButton
,然后执行
override func viewDidLoad() {
super.viewDidLoad()
titleButton.translatesAutoresizingMaskIntoConstraints = false
}
Swift
4.2 版本
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 80, height: 30))
imageView.image = image
imageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 30).isActive = true
imageView.contentMode = .scaleAspectFit
navigationItem.titleView = imageView
在 Swiftify 的帮助下转换。
这个 class 可以解决问题。确保将自定义视图的 class 设置为这个:
import UIKit
class TitleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
super.init(coder: coder)
translatesAutoresizingMaskIntoConstraints = false
}
override var intrinsicContentSize: CGSize {
CGSize(width: UIView.layoutFittingExpandedSize.width, height: self.bounds.height)
}
}
关于设置导航控制器
您所要做的就是将 translatesAutoresizingMaskIntoConstraints 设置为 false
check apple doc
Note that the autoresizing mask constraints fully specify the view’s size and position; therefore, you cannot add additional constraints to modify this size or position without introducing conflicts. If you want to use Auto Layout to dynamically calculate the size and position of your view, you must set this property to false
然后将其指定为您想要的宽度
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false
lazy var titleWidth = view.frame.size.width / 2
navigationItem.titleView?.constrainWidth(constant: titleWidth)
在 iOS11 上看到一个 navigationItem.titleView 的行为,其中 titleView 的宽度不是屏幕的全宽。
我有一个自定义视图,我将其设置为 titleView。在 iOS11 之前,视图将填满导航栏区域。但是 iOS11 它没有调整大小以填充屏幕的宽度。
我试过在设置 titleView 之前设置视图的框架,但没有成功。我也试图强制 titleViews 超级视图布局约束,但没有成功。
附上截图:
iOS10:
iOS11:
还有其他人遇到过这种情况吗?
我明白了。我必须覆盖视图和文本字段的 intrinsicContentSize getter。
我将宽度设置为 CGFloat.greatestFiniteMagnitude,因此它始终与屏幕一样宽。
更新:
由于我已经在这个问题上花了几个小时,希望其他人能够通过将所有事情紧密结合起来更快地赶上来
我创建了 TitleView
的自定义子 class,名为 CustomTitleView
,代码如下:
import UIKit
class CustomTitleView: UIView {
override var intrinsicContentSize: CGSize {
return UIView.layoutFittingExpandedSize
}
}
我从一开始就错过的最重要的部分是:
将 intrinsicContentSize
设置为 UILayoutFittingExpandedSize
也可以正常工作
这里使用@falkon 的回答代码:
将此代码添加到用作 titleView 的视图中
override var intrinsicContentSize: CGSize {
return UILayoutFittingExpandedSize
}
我遇到了同样的问题,但是将 UIImage
设置为 navigationItem
titleView
我所做的是使用以下方法将图像缩放到所需的大小:
-(UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
并按如下方式调用:
-(void)setHeaderImage{
UIImage * image = [self imageWithImage:[UIImage imageNamed:@"headerImage"] scaledToSize:CGSizeMake(150, 27)];
UIImageView * imageView = [[UIImageView alloc]initWithImage:image];
imageView.frame = CGRectMake(0, 0, 150, 27);
imageView.contentMode = UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imageView;
}
最重要的是你需要覆盖 customTitleView 作为你的 titleView:
self.navigationItem.titleView = [self titleView];
#pragma mark - getter
- (UIView *)titleView {
UIView *navTitleView = [HFCalenderTitleView new];
navTitleView.frame = CGRectMake(0.0, 0.0, HorizontalFrom750(200.0), 44.0);
[navTitleView addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(navTitleView);
}];
CGFloat btnWidth = 30.0;
[navTitleView addSubview:self.previousButton];
self.previousButton.imageEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 15.0);
[self.previousButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(navTitleView);
make.top.bottom.equalTo(navTitleView);
make.width.equalTo(@(btnWidth));
}];
[navTitleView addSubview:self.nextBtn];
self.nextBtn.imageEdgeInsets = UIEdgeInsetsMake(0.0, 15.0, 0.0, 0.0);
[self.nextBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(navTitleView);
make.top.bottom.equalTo(navTitleView);
make.width.equalTo(@(btnWidth));
}];
return navTitleView;
}
#pragma mark - customTitleView
#import "HFCalenderTitleView.h"
@implementation HFCalenderTitleView
- (CGSize)intrinsicContentSize{
return CGSizeMake(HorizontalFrom750(200.0), 40); // the target size
}
通过创建 UIView 的子类并将其分配给 UINavigationController 的标题视图来修复它
Objective-C:
#import "FLWCustomTitleView.h"
@implementation FLWCustomTitleView
- (CGSize )intrinsicContentSize {
return UILayoutFittingExpandedSize;
}
@end
如果您不想覆盖 intrinsicContentSize
,也可以使用约束。这是 SnapKit 的演示
self.navigationItem.titleView = titleView
if #available(iOS 11, *) {
titleView.snp.makeConstraints({ (make) in
make.width.equalTo(250) // fixed width
make.height.equalTo(30) // less than 44(height of naviagtion bar)
})
}else {
titleView.frame = ...
}
但是如果导航栏的任一侧(左侧或右侧)有多个导航栏项目,您应该使用 intrinsicContentSize;
当您在 CustomTitleView 中有一个 UIView 作为子视图时,intrinsicContentSize 解决方案不起作用,仅对 XCODE 9 和 iOS 11 中的我而言。所以我确实喜欢下面的内容,对我来说效果很好,这可能对某人有帮助。
@interface CustomTitleView : UIView
@property (weak, nonatomic) IBOutlet UIView *doubleTitleView;
@end
@implementation CustomTitleView
- (void)awakeFromNib {
[super awakeFromNib];
int width = _doubleTitleView.frame.size.width;
int height = _doubleTitleView.frame.size.height;
if (width != 0 && height != 0) {
NSLayoutConstraint *widthConstraint = [_doubleTitleView.widthAnchor constraintEqualToConstant:width];
NSLayoutConstraint *heightConstraint = [_doubleTitleView.heightAnchor constraintEqualToConstant:height];
[_doubleTitleView addConstraint:heightConstraint];
[_doubleTitleView addConstraint:widthConstraint];
[heightConstraint setActive:TRUE];
[widthConstraint setActive:TRUE];
}
}
return UILayoutFittingExpandedSize
对我没有帮助,因为视图被垂直添加了几次以填充布局。
解决方案是将自定义视图设置宽度中的 intrinsicContentSize
覆盖为最大屏幕宽度:
- (CGSize)intrinsicContentSize {
//fills empty space. View will be resized to be smaller, but if it is too small - then it stays too small
CGRect frame = self.frame;
frame.size.width = MAX(SCREEN_WIDTH, SCREEN_HEIGHT);
return frame.size;
}
我必须将 UIImageView 设置为 navigationItem.titleView。纵横比确实合适,但 intrinsicContentSize 使其变大了。 缩放图像导致图像质量差。 设置布局锚点对我有用:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 30)];
[imageView setImage:image];
[imageView.widthAnchor constraintEqualToConstant:80].active = YES;
[imageView.heightAnchor constraintEqualToConstant:30].active = YES;
[imageView setContentMode:UIViewContentModeScaleAspectFit];
self.navigationItem.titleView = imageView;
尝试使用标准 UISearchBar / UISearchController
实际上你需要做的 - 如果你可以使用标准的 UISearchBar / UISearchController 是按照以下方式显示搜索栏,它尊重安全区域,因此在 iPhone X 和每个设备方向:
func presentSearchController() {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.text = "any text"
if #available(iOS 11.0, *) {
self.navigationItem.searchController = searchController
searchController.isActive = true
} else {
present(searchController, animated: true, completion: nil)
}
}
参考资料
https://developer.apple.com/videos/play/fall2017/201/ https://medium.com/@PavelGnatyuk/large-title-and-search-in-ios-11-514d5e020cee
对现有答案的补充:
如果您的自定义标题视图是 默认情况下已经具有固有内容大小(.zero
除外)的视图,例如 UILabel
,一个UITextView
或一个UIButton
,你可以简单地设置
yourCustomTitleView.translatesAutoresizingMaskIntoConstraints = false
并且它会自动调整以仅包含其内容,但不会与左右项目视图重叠。
例如,您可以在 Interface Builder 中将一个按钮拖到导航栏的标题视图区域,在您的视图控制器中为其创建一个插座 titleButton
,然后执行
override func viewDidLoad() {
super.viewDidLoad()
titleButton.translatesAutoresizingMaskIntoConstraints = false
}
Swift
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 80, height: 30))
imageView.image = image
imageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 30).isActive = true
imageView.contentMode = .scaleAspectFit
navigationItem.titleView = imageView
在 Swiftify 的帮助下转换。
这个 class 可以解决问题。确保将自定义视图的 class 设置为这个:
import UIKit
class TitleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
super.init(coder: coder)
translatesAutoresizingMaskIntoConstraints = false
}
override var intrinsicContentSize: CGSize {
CGSize(width: UIView.layoutFittingExpandedSize.width, height: self.bounds.height)
}
}
关于设置导航控制器
您所要做的就是将 translatesAutoresizingMaskIntoConstraints 设置为 false check apple doc
Note that the autoresizing mask constraints fully specify the view’s size and position; therefore, you cannot add additional constraints to modify this size or position without introducing conflicts. If you want to use Auto Layout to dynamically calculate the size and position of your view, you must set this property to false
然后将其指定为您想要的宽度
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false
lazy var titleWidth = view.frame.size.width / 2
navigationItem.titleView?.constrainWidth(constant: titleWidth)