Swift Как узнать, закончил ли макет после layoutIfNeeded () - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть математическая программа для iPad, использующая библиотеку больших чисел, которая выполняет вычисления, а затем обновляет массив до 20 000 UILabels в UIView (myView) на основе этих вычислений.

Расчеты могут занять около 5 секунд, в течение которых для backgroundColor каждого из UILabels устанавливается цвет. Поскольку на экране ничего не происходит, у меня мигает UILabel inProgressLabel, который информирует пользователя о том, что система рассчитывает. Затем я вызываю layoutIfNeeded с мыслью, что по окончании экран будет обновлен. Наконец я выключаю мигающую UILabel.

Вот псевдокод:

inProgressLabel.turnOnBlinking()

for row in 0..<rowCount
{

    for col in 0..<colCount
    {

        // perform some calculation
        let z = buttonArray[row][col].performCalculation() 


        //now set the Label background based on the result of the calculation
        buttonArray[row][col].setLabelBackground(z)
    }

}

myView.layoutIfNeeded()   

inProgressLabel.turnOffBlinking()

Насколько я понимаю, layoutIfNeeded () является синхронным. Таким образом, экран будет обновляться, а затем и только тогда мигающий inProgressLabel будет отключен. Однако inProgressLabel фактически отключается сразу после вызова layoutIfNeeded, а затем может потребоваться еще пять секунд для обновления массива UILabels.

Я подумал, что, возможно, это происходит, потому что обновление происходит в другом потоке. Если это так, есть ли способ точно знать, когда UIView и массив UILabels закончили обновление (отображение), чтобы я мог затем отключить мигающую UILabel?

Большое спасибо заранее.

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

Элементы управления UIKit не являются поточно-ориентированными. Обновление UILabels из фона приводит к неопределенному поведению.

layoutIfNeeded выполняет все отложенные задачи макета и возвращает. У него нет волшебного предвидения каких-либо изменений, которые вы планируете внести в будущем.

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

Также маловероятно, что 20 000 UILabels подходят; рассмотрите использование представления таблицы или представления коллекции. Как правило, вы должны стараться иметь активные элементы управления только для того, что в данный момент отображается на экране, и оба этих представления предлагают способы очень легко управлять этим активным набором. Каждое представление имеет объем памяти, потому что содержимое представлений буферизируется - вы ухудшите свою производительность и производительность других запущенных приложений, если излишне сжимаете память.

0 голосов
/ 30 апреля 2018

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

let dispatchGroup = DispatchGroup()

for row in 0..<rowCount
{

    for col in 0..<colCount
    {

        dispatchGroup.enter()

        self.doCalcAndUpdateLbl(index: i) { (finish) in

            dispatchGroup.leave()
        }
    }
}

dispatchGroup.notify(queue: .main) {

    myView.layoutIfNeeded()

    inProgressLabel.turnOffBlinking()
}
...