Неправильное вращение UIImageView с использованием CGAffineTransform

У меня есть 4 раздела, каждый раздел имеет 2 вложенных строки. Я открываю ряды, нажимая на каждый раздел.

Когда я нажимаю на раздел, я хочу, чтобы его аксессуар (chevron.right, выполненный как UIImageView) поворачивался по часовой стрелке на 90 градусов с плавной анимацией, а когда я снова нажимаю на тот же раздел, - поворачивался назад, против часовой стрелки на 90 градусов.

У меня есть переменная с именем isOpened (bool, false по умолчанию), которую я меняю с false на true и возвращаю каждое касание в didSelectRowAt. На основании этого показываются все вложенные ячейки:

   override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      if indexPath.row == 0 {
        sections[indexPath.section].isOpened = !sections[indexPath.section].isOpened
        tableView.reloadSections([indexPath.section], with: .none)
      }
   }

Таким же методом я пытаюсь повернуть шеврон:

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.row == 0 {
        sections[indexPath.section].isOpened = !sections[indexPath.section].isOpened
        tableView.reloadSections([indexPath.section], with: .none)
        
        guard let cell = tableView.cellForRow(at: indexPath) as? MainSortTableViewCell else { return }

        if sections[indexPath.section].isOpened {
            UIView.animate(withDuration: 1.0) {
                cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2)
            }
        } else {
            UIView.animate(withDuration: 1.0) {
                cell.chevronImage.transform = CGAffineTransform(rotationAngle: -.pi/2)
            }
        }
     }

Но при таком подходе я сталкиваюсь с парой проблем:

  1. При первом касании он анимирует шеврон вниз на 90 градусов, как мне нужно, но когда я снова нажимаю, он поворачивает шеврон исходный (лицом вправо), что приводит к тому, что вместо этого шеврон смотрит вверх (-pi / 2). Как исправить поведение и повернуть шеврон из его нового положения (с того момента, когда его лицевая сторона опущена обратно в исходное право)?
  2. Когда я пытаюсь нажать после первого вращения, анимация больше никогда не запустится. Переход мгновенный. Как это исправить?

Вот GIF с проблемным поведением: НАЖМИТЕ

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
1
0
24
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы неправильно поняли, что означает свойство transform. transform представляет преобразование вида из его положения оригинальный (в данном случае лицом вправо), поэтому установка его в CGAffineTransform(rotationAngle: -.pi / 2) приводит к преобразованию вида таким образом, что он поворачивается на -.pi / 2 радиан из положения оригинальный, т. е. лицом вверх.

Чтобы добиться желаемого поворота, вы можете либо сочинять этого -.pi/2 поворота с текущим преобразованием представления, либо просто установить transform на .identity (вообще без преобразования, из исходного положения):

cell.chevronImage.transform = .identity
// or
cell.chevronImage.transform = cell.chevronImage.transform.rotated(by: -.pi / 2)

Я подозреваю, что вы не добавили этот код вращения в cellForRowAtIndexPath. Пожалуйста, сделай это, только без анимации. В противном случае вы столкнетесь с такими проблемами, как сброс шеврона после прокрутки представления таблицы (смотрите также). Однако это будет означать, что он будет вращаться слишком рано, когда вы вызываете reloadSections. Самый простой способ решить эту проблему — переместить reloadSections после анимации:

UIView.animate(withDuration: 1.0) {
    if sections[indexPath.section].isOpened {
        cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2)
    } else {
        cell.chevronImage.transform = .identity
    }
} completion: {_ in
    self.tableView.reloadSections([indexPath.section], with: .none)
}

Привет дворник! Большое спасибо за такой подробный ответ. Я действительно неправильно понял концепцию трансформации, поэтому спасибо, что разъяснили ее мне! Можно еще вопрос: а что если мне нужно открывать вложенные ячейки не после завершения анимации, а синхронно с ней? Например, если вы попытаетесь сделать это в приложении «Файлы», шеврон будет анимирован, и вложенные ячейки будут показаны одновременно: cln.sh/z0LM6E Как я думаю, мне следует перезагрузить разделы в выражении if/else, где я поворачиваю шеврон, но это не работает должным образом: cln.sh/RtDczD

artexhibit 10.04.2022 13:36

@artexhibit Я чувствую, что это слишком выходит за рамки этого вопроса, который в основном касался transform. Первое, что я бы попробовал, это использовать insertRows и deleteRows, чтобы просто добавить/удалить нужные строки, а не перезагружать весь раздел. Таким образом, вам не нужно делать это после завершения анимации. Если это не сработает, я предлагаю вам опубликовать другой вопрос.

Sweeper 10.04.2022 13:41

Конечно, спасибо большое, буду копать в этом направлении

artexhibit 10.04.2022 13:43

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