Как я могу исправить проблему «removeConstraint () не работает» с моим UICollectionView? - PullRequest
0 голосов
/ 07 апреля 2019

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

Я использую Xcode 10.1 и Swift 4.2 без раскадровок. В моем MainViewController я создаю и размещаю UICollectionView с именем «ticketsView» из функций, вызываемых viewDidLoad(). После addChild() и addSubview() я вызываю функцию с именем applyTicketsViewConstraints(), которая определяет текущую ориентацию устройства, а затем (как ожидается) удаляет все существующие ограничения и применяет соответствующий набор ограничений для текущей ориентации устройства.

Я переопределяю viewWillTransition:toSize:withCoordinator(), который вызывает одну и ту же функцию applyTicketsViewConstraints() из замыкания координатора при каждом повороте устройства.

При запуске приложения ограничения корректно применяются на основе исходной ориентации устройства. При первом повороте устройства на 90 градусов дисплей также успешно изменяется, но любое вращение после этого приводит к сообщению об ошибке «[LayoutConstraints] Невозможно одновременно удовлетворить ограничения».

Я попытался перебрать существующие ограничения ticketsView и удалить их по одному, и я попытался удалить их все сразу, но ни один из подходов не приводит к фактическому удалению ограничений. Похоже, они оба работают успешно, но после их завершения ограничения остаются в силе.

    func createTicketCollectionWindow() {
        ticketsLayout = UICollectionViewFlowLayout()
        ticketsVC = TicketCollectionController(collectionViewLayout: ticketsLayout)
        ticketsVC?.collectionView?.backgroundColor = UIColor.darkGray
        ticketsView = ticketsVC!.view
        ticketsView.translatesAutoresizingMaskIntoConstraints = false
        addChild(ticketsVC!)
        self.view.addSubview(ticketsView)
        applyTicketsViewConstraints()
    }
    func applyTicketsViewConstraints() {
        let safe = self.view.safeAreaLayoutGuide
        let deviceSize = UIScreen.main.bounds.size
        for individualConstraint in ticketsView.constraints {
            print("TicketViewConstraints:   Removing Constraint: \(individualConstraint)" )
            // ticketsView.removeConstraint(individualConstraint)
        }
        ticketsView.removeConstraints(ticketsView.constraints)
        // self.view.removeConstraints(ticketsView.constraints)
        if deviceSize.width < deviceSize.height {
            print("TicketsView Constraint:  Device is in PORTRAIT orientation!")
            ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width).isActive = true
            ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ).isActive = true
            ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0).isActive = true
            ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75).isActive = true
        } else {
            print("TicketsView Constraint:  Device is in LANDSCAPE orientation!")
            ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width).isActive = true
            ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ).isActive = true
            ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0).isActive = true
            ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65).isActive = true
        }
        ticketsView.layoutIfNeeded()
    }
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: { (_) in
            self.applyTicketsViewConstraints()
            self.ticketsVC?.collectionView.reloadData()
        }) { (_) in
            self.ticketsView.layoutIfNeeded()
        }
    }

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

Ответы [ 3 ]

1 голос
/ 07 апреля 2019

Вы можете попробовать

var portrait = [NSLayoutConstraint]()
var landscape = [NSLayoutConstraint]()

let porCon1  = ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width) 
let porCon2  =  ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ) 
let porCon3  = ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0) 
let porCon4  = ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75) 

portrait = [porCon1,porCon2,porCon3,porCon4]

let landCon1  = ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width) 
let landCon1  = ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ) 
let landCon1  = ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0) 
let landCon1  = ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65) 

landscape = [landCon1,landCon2,landCo3,landCon4]

func setPortrait(_ por:Bool) {  
    portrait.forEach { $0.isActive = por }
    landscape.forEach { $0.isActive = !por } 
}

Может быть также

NSLayoutConstraint.deActivate(portrait)
NSLayoutConstraint.deActivate(landscape)
if por {
   NSLayoutConstraint.activate(portrait)
}
else { 
  NSLayoutConstraint.activate(landscape)
}
0 голосов
/ 07 апреля 2019

Первые 2 ответа, которые были опубликованы, объединены, чтобы дать мне ответ, который мне нужен.@Sh_Khan дал мне указание использовать массив NSLayoutConstraint.@Wurzel указал, что я должен использовать функции NSLayoutConstraint.activate () и .deactivate (), чтобы включить / отключить всю группу ограничений одновременно.

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

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

Мой оригинальный код теперь становится:

    func applyTicketsViewConstraints() {
        let safe = self.view.safeAreaLayoutGuide
        let deviceSize = UIScreen.main.bounds.size

        NSLayoutConstraint.deactivate(portraitTicketsViewConstraints)
        NSLayoutConstraint.deactivate(landscapeTicketsViewConstraints)

        if deviceSize.width < deviceSize.height {
            print("TicketsView Constraint:  Device Size is in PORTRAIT orientation!")
            portraitTicketsViewConstraints = [
                ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width),
                ticketsView.trailingAnchor.constraint(equalTo: safe.trailingAnchor, constant: 0 ),
                ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0),
                ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75)
            ]
            NSLayoutConstraint.activate(portraitTicketsViewConstraints)
        } else {
            print("TicketsView Constraint:  Device Size is in LANDSCAPE orientation!")
            landscapeTicketsViewConstraints = [
                ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width),
                ticketsView.trailingAnchor.constraint(equalTo: safe.trailingAnchor, constant: 0 ),
                ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0),
                ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65)
            ]
            NSLayoutConstraint.activate(landscapeTicketsViewConstraints)
        }
        ticketsView.layoutIfNeeded()
    }
}
0 голосов
/ 07 апреля 2019

Я считаю, что вам нужно работать с активировать / деактивировать ограничения, например:

Создать переменные для них и установить значения ограничений в вашем setupConstrainsMethod.var notRotatedYConstraint: NSLayoutConstraint!

        var rotatedYConstraint: NSLayoutConstraint!

в viewdidload: NSLayoutConstraint.activate ([notRotatedYConstraint])

В вашей функции для случая, когда iPhone поворачивается.NSLayoutConstraint.deactivate ([notRotatedYConstraint]) NSLayoutConstraint.activate ([rotatedYConstraint])

...