Это сложнее, чем должно быть

Выпуск iOS 15 принес некоторые столь необходимые улучшения в SwiftUI. Теперь у нас есть встроенные панели поиска и даже поддержка уценки для текстовых представлений.

Но каким-то образом SwiftUI ScrollView все еще немного глючит. Этот парень из Reddit выразился очень красиво:

Что-то вроде изменения цвета фона SwiftUI ScrollView по-прежнему представляет собой довольно сложную задачу. Интуитивно понятно, что нужно использовать модификатор .background непосредственно на ScrollView, например:

ScrollView {
    Text(“Hello, World”)
}
.background(Color.purple)

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

  1. Ваш ScrollView должен находиться непосредственно внутри NavigationView (без навигации)
  2. Ваш ScrollViews контент должен масштабироваться по горизонтали, используя HStack с Spacers, чтобы заставить ScrollView заполнить всю ширину экрана (или установить его рамку вручную).

Если какое-либо из этих предостережений не будет выполнено, вы получите нежелательное поведение.

Либо ваш ScrollView отображается посередине с цветом, отличным от фона фактического вида (менее проблематично), либо если вы не поместите свой ScrollView напрямуюв NavigationView, ScrollView не будет автоматически сворачивать панель навигации. — она идет под ним — как будто панель навигации прозрачная.

Определенно не идеальное решение!

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

Это означает, что у меня не было возможности использовать другой NavigationView вокруг ScrollView (см. предостережение 2). И обертывание моего ScrollView двумя слоями NavigationViews (и, может быть, скрытие одного?) просто не кричит мне о стабильности.

Если вы гуглили эти проблемы так же долго, как и я, вы, вероятно, сталкивались с решениями, использующими модификатор appearance().

Обычно что-то вроде ниже, когда я хотел, чтобы мой фон ScrollViews был изменен на розовый:

UIScrollView.appearance().backgroundColor = UIColor.systemPink

При первом осмотре все работает отлично. Вы получаете именно то поведение, которое ожидаете от UIKit ScrollView. Панель навигации схлопывается и все хорошо…

Пока вы не попытаетесь использовать TextField. При редактировании TextField цвет фона вашего TextField меняется на цвет вашего ScrollView. Вы можете увидеть пример того, как это происходит ниже (справа):

Почему, черт возьми, это происходит? Не имею представления. Использование модификатора appearance() вызывает неодобрение, но это единственный способ заставить некоторые вещи работать в SwiftUI.

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

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

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

После целой кучи возни и испытаний я нашел модификатор appearance(), который, кажется, содержит урон.

Фактически вы можете указать условия для модификаторов appearance. Это полезно для UIKit, потому что вы можете указать контроллеры представления, где вы хотите применить стили. Вы также можете указать UITraitCollection, чтобы отфильтровать, где стили применяются еще дальше.

Путем проб и ошибок я нашел комбинацию, которая работает очень хорошо:

В этом есть две части:

Во-первых, мы указываем userInterfaceLevel для применения только к областям, где уровень интерфейса считается base.

Документы Apple говорят, что это в основном только ваш основной контент Windows. Также есть значение elevated, которое, как я могу предположить, равно Alerts или Sheets.

Затем мы указываем свойство whenContainedInInstancesOf как:

...whenContainedInInstancesOf: [UINavigationController.self]...

Сочетание этих условий останавливает модификатор ScrollView appearance(), ломающий наш TextFields и наши клавиатуры.

Указание свойства whenContainedInInstancesOf фиксирует цвет автоисправления клавиатуры на серый по умолчанию. Я предполагаю, что это связано с тем, что клавиатура не содержится в навигационном контроллере, поскольку она не является частью нашего приложения (она представлена ​​iOS).

И затем, указание userInterfaceLevel исправляет фон TextField. Я не уверен, почему это так. Можно подумать, что любое представление в вашем приложении можно рассматривать как уровень пользовательского интерфейса base. Возможно, та часть TextField, которая меняет цвет фона, считается elevated.

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

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

Надеюсь, эта статья помогла вам сориентироваться в хитром и коварном мире SwiftUI. Будем надеяться, что в iOS 16 ситуация улучшится (постучим по дереву).

Спасибо за прочтение.

Want to Connect With the Author?
Give me a follow on Twitter to see what I’m working on.