iOS 11 navigationItem.titleView Ширина не установлена

Наблюдение за поведением на iOS11 с навигационным элементом Item.titleView, где ширина titleView не является полной шириной экрана.

У меня есть пользовательский вид, который я установил как titleView. До iOS11 представление будет заполнять область панели навигации. Но iOS 11 не изменяет размеры, чтобы заполнить ширину экрана.

Я пытался установить рамку представления перед настройкой titleView, но не повезло. Я пытался заставить суперпредставление titleViews также ограничить макет, но безуспешно.

Скриншоты прилагаются:

iOS10:

iOS11:

Кто-нибудь еще испытывает это?

15 ответов

Решение

Я понял. Мне пришлось переопределить метод получения intrinsicContentSize для представления и текстового поля.

Я установил ширину в CGFloat.greatestFiniteMagnitude, чтобы она всегда была такой же ширины, как и экран.

Обновить:

Поскольку я потратил пару часов на эту проблему, надеюсь, что кто-то еще наверстает упущенное, собрав все вместе

Я создал собственный подкласс TitleView, называется CustomTitleViewвот код:

import UIKit

class CustomTitleView: UIView {

  override var intrinsicContentSize: CGSize {
    return UILayoutFittingExpandedSize
  }
}

и самая важная часть, которую я пропустил с самого начала, заключалась в следующем:

Используя ответ @falkon, вот код:

Добавьте этот код в представление, которое используется в качестве titleView

override var intrinsicContentSize: CGSize {
    return UILayoutFittingExpandedSize
} 

Исправили это, создав подкласс UIView и присвоив его заголовку UINavigationController

Objective-C:

#import "FLWCustomTitleView.h"

@implementation FLWCustomTitleView

- (CGSize )intrinsicContentSize {
  return UILayoutFittingExpandedSize;
}

@end

введите описание изображения здесь введите описание изображения здесь

Установка intrinsicContentSize в UILayoutFittingExpandedSize works fine as well

Я должен был соответствовать UIImageView как навигация Item.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;

Дополнение к существующим ответам:

Если ваш пользовательский вид заголовка - это вид, который по умолчанию уже имеет внутренний размер контента (кроме .zero), например UILabel, UITextView или UIButton, вы можете просто установить

yourCustomTitleView.translatesAutoresizingMaskIntoConstraints = false

и он автоматически настроится так, чтобы просто заключать его содержимое, но никогда не совпадать с левым и правым представлениями элементов.


Например, вы можете перетащить кнопку в область просмотра заголовка панели навигации в Интерфейсном Разработчике, создать розетку 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.

Этот класс сделает свое дело. Убедитесь, что вы установили класс своего пользовательского представления на этот:

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)
    }

}

Вы также можете использовать ограничения, если вы не хотите переопределять 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;

Когда у вас есть UIView как подпредставление внутри CustomTitleView, решение 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];
    }
}

Наиболее важным является то, что вам нужно перезаписать 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
}

введите описание изображения здесь

введите описание изображения здесь

У меня была такая же проблема, но с настройкой UIImage как navigationItemtitleView

То, что я сделал, я масштабировал изображение до необходимого размера, используя ниже:

-(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;
}

по настройке-настройке вашего навигационного контроллера

все, что вам нужно сделать, это установить для translatesAutoresizingMaskIntoConstraints значение falsecheck apple doc

Обратите внимание, что ограничения маски автоматического изменения размера полностью определяют размер и положение представления; следовательно, вы не можете добавлять дополнительные ограничения для изменения этого размера или положения без возникновения конфликтов. Если вы хотите использовать Auto Layout для динамического расчета размера и положения вашего представления, вы должны установить для этого свойства значение false.

затем назначьте ему желаемую ширину

      navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false

lazy var titleWidth =  view.frame.size.width / 2

navigationItem.titleView?.constrainWidth(constant: titleWidth)

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;
}

Попробуйте использовать стандартный 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

Другие вопросы по тегам