View.traitCollection.horizontalSizeClass возвращает undefined (0) в viewDidLoad

Я использую UITextView внутри UIPageViewController, и я хочу определить размер шрифта на основе класса размера устройства.

Первый слайд просмотра страницы загружается в ViewDidLoad так (viewControllerAtIndex(0)):

override func viewDidLoad() {
    super.viewDidLoad()

    //Some unrelated code here

    // Page View Controller for Questions Slider
    questionPageVC = storyboard?.instantiateViewControllerWithIdentifier("QuestionPageView") as? UIPageViewController
    questionPageVC!.dataSource = self;
    questionPageVC!.delegate = self;

    let startingViewController : QuestionContentViewController = viewControllerAtIndex(0) as QuestionContentViewController
    var viewControllers = [startingViewController]

    questionPageVC!.setViewControllers(viewControllers, direction: .Forward, animated: true, completion: nil)

    let sliderHeight = view.frame.size.height * 0.5
    questionPageVC!.view.frame = CGRectMake(20, 70,
     view.frame.size.width-40, sliderHeight)

    addChildViewController(questionPageVC!)
    view.addSubview(questionPageVC!.view!)
    questionPageVC?.didMoveToParentViewController(self)

    var pageControl : UIPageControl = UIPageControl.appearance()
    pageControl.pageIndicatorTintColor = UIColor.lightGrayColor()
    pageControl.currentPageIndicatorTintColor = UIColor.blackColor()
    pageControl.backgroundColor = UIColor.whiteColor()

    // Some more code here
}

И затем, в viewControllerAtIndex:

private func viewControllerAtIndex(index: Int) -> QuestionContentViewController {
    var pcvc : QuestionContentViewController = storyboard?.instantiateViewControllerWithIdentifier("QuestionContentView") as! QuestionContentViewController

    var fontSize = ""

    if (view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact) {
        fontSize = "20"
    } else {
        fontSize = "28"
    }

    pcvc.questionString = TextFormatter(string: fontSize + questionsArray[index]).formattedString
    pcvc.questionIndex = index

    return pcvc

}

Проблема в том, что самый первый слайд, который был вызван в viewDidLoad, всегда использует размер шрифта в предложении else.

Если я печатаю view.traitCollection.horizontalSizeClass, для этого первого слайда я получаю 0 (UIUserInterfaceSizeClassUnspecified), для последующих слайдов я получаю правильный размер.

Я попробовал переместить все это в "viewWillAppear", а затем с UIPageViewController произойдут странные вещи (дополнительный слайд с неправильным размером текста за другими слайдами)

Ответ 1

Проблема в том, что viewDidLoad слишком рано спрашивать о коллекции представлений. Это связано с тем, что коллекция признаков представления представляет собой функцию, полученную из иерархии представлений, в среде, в которой она находится. Но в viewDidLoad представление не имеет среды: оно еще не находится в иерархии представлений. Он загружен, что означает, что он существует: теперь у контроллера вида есть представление. Но он еще не был помещен в интерфейс, и он не будет помещен в интерфейс до тех пор, пока не будет viewWillAppear:, который появится позже в последовательности событий.

Однако диспетчер представлений также имеет коллекцию признаков, и у него есть среда: к моменту вызова viewDidLoad контроллер представления является частью иерархии контроллера представления. Поэтому самым простым (и правильным) решением является запрос traitCollection self, а не view. Просто скажите self.traitCollection, где у вас теперь есть view.traitCollection, и все будет хорошо.

(Ваше решение, запрашивающее экран для его коллекции признаков, может работать, но оно не является надежным и не является правильным. Это связано с тем, что родительский контроллер просмотра может изменять коллекцию признаков своего child, и если вы обойдете правильный подход и спросите экран, прямо, вы не получите правильную коллекцию признаков.)

Ответ 2

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

if (UIScreen.mainScreen().traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact) {
    fontSize = "20"
} else {
    fontSize = "28"
}

Ответ 3

Вам будет лучше перемещать этот код в метод viewWillAppear, так как в viewDidLoad представление ViewController еще не добавлено в иерархию, и вы можете получить пустую коллекцию признаков.

Ответ 4

В моем случае мне нужно было, чтобы мой взгляд знал о horizontalSizeClass, поэтому доступ к traitCollection UIScreen был заманчивым, но не поощрялся, поэтому у меня было что-то вроде этого:

override func layoutSubviews() {
    super.layoutSubviews()
    print("\(self.traitCollection.horizontalSizeClass.rawValue)")

    switch self.traitCollection.horizontalSizeClass {
    case .regular, .unspecified:
        fontSize = 28

    case .compact:
        fontSize = 20
    }
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    print("\(self.traitCollection.horizontalSizeClass.rawValue)")

    guard let previousTraitCollection = previousTraitCollection else { return }

    if self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass {
        layoutIfNeeded()
    }
}