iOS: Навигация по-новому

от автора

С каждый днем все больше разработчиков IOS стремятся свои новые проекты начинать с использованием SwiftUI. И здесь перед ними возникает проблемы в виде реализации устоявшихся представлений о навигации в iOS. Предлагаемые решения от Apple работают весьма часто довольно криво. Это понимают и в самой Apple. По мере развития SwiftUI основной компонент навигации NavigationView был заменен на NavigationStack. И это не просто переименование. Те кто уже использовал NavigationView не готовы от него отказаться, так как его реализация лежала через боль и слезы. Те же кто только входит в мир SUI либо наталкиваются на рекомендации создавать кастомную навигацию, либо смотрят на статьи как разруливать проблемы NavigationView. Новая альтернатива не всем пришлась по-душе, так как на WWDC не продемонстрировали его с лучшей стороны. А она есть. И это хорошая новость! Apple, наконец, освоила паттерн Navigator, которым конкуренты пользовались более 10 лет!<cut />

В чем суть: теперь навигация становится возможной даже при помощи передачи пути для навигации. Те кто пользовался DeepLink или UniversalLink возрадуются. Теперь и на их улице будет праздник.

Hidden text

Это не значит что, раньше это было невозможно, но теперь не приходится для этого устраивать танцы с бубном.

Чтоб продемонстрировать идею, был набросан минимальный проект, включающий пять экранов, с незамысловатыми названиями: first, second, third и fourh. Эти экраны были объединены следующей схемой переходов: 

Здесь, сплошной линией со стрелкой обозначен прямой переход на указанный экран. Толстой стрелкой показан переход на четвертый экран по пути навигации. Переходы по нажатию на «Back» и переход на главный экран не обозначены, чтоб не захламлять схему.

Вместо набившим оскомину NavigationLink в приложении был использован обычный Button, так как он значительно лучше поддается кастомизации.

Вся навигация сводится к передаче массива с условными названиями экранов в переменную пути. NavigarionStack пройдет все цепочку навигации автоматически, и покажет последний экран в цепочке.

Так для того, чтоб показать экран «Fourth» с сохранением всей цепи навигации достаточно в переменную передать массив [«first», «second», «third», «fourh»]. Соответственно, и возврат по «Back» будет происходить в обратном порядке.

Button { model.path = ["first", "second", "third", "fourth"] }                          label: { ButtonContent("The furthest view") }

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

В отличите от UIKit, NavigationStack не хранит в себе состояния предыдущих экранов. Таким образом, при возврате — View и его ViewModel будет воссоздана с нуля – это следует учитывать при создании архитектуры UI, когда необходимо сохранить пользовательские данные, или вернуть состояние Scroll / таблицы к предыдущей ячейке.

Ключевой особенностью реализации всей схемы является метод, который возвращает View по его имени в пути. Понятное дело, что в реальном проекте именование лучше осуществлять через Enum, но для демонстрации это не имеет большого значения. Если сравнить со статьей про кастомную реализацию навигации при помощи координатора – вполне очевидно, что такая навигация значительно проще.

class Coordinator: ObservableObject {     @Published var path: [String] = []          func resolve(pathItem:String) -> some View {         Group {             switch pathItem {             case "first": FirstView()             case "second": SecondView()             case "third": ThirdView()             case "fourth": FourthView()             default:                 EmptyView()             }         }     } }

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

Button { model.path = [] } label: { ButtonContent("Root View") }

Скорее всего, NavigationStack все еще хранит в себе множество неприятных сюрпризов, доставшихся по наследству от NavigationView. Первым бросается в глаза то, что при переходе на более чем один уровень вложенности – слетает анимация. Это происходит если путь заменяется единственным элементом в массиве. При добавлении нового элемента в массив  – такого не происходит.

Код доступен на GitHub

Обсудить можно на телеграмм канале.

Предыдущая статья о кастомной навигации доступна на хабре.


ссылка на оригинал статьи https://habr.com/ru/post/694946/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *