Как отключить bounce в UIScrollView
Что проверяют
Знаете ли вы свойства UIScrollView, которые отвечают за отскок, и разницу между ними. Вопрос простой, поэтому следом почти сразу спрашивают, как выключить отскок по одной оси и что сломается вместе с ним.
Как отвечать
Главное свойство bounces, по умолчанию true. Строка scrollView.bounces = false выключает отскок на краях контента по обеим осям. Это работает и для UITableView, UICollectionView, и для WKWebView через его scrollView.
Соседние свойства часто путают:
alwaysBounceVerticalиalwaysBounceHorizontal: по умолчаниюfalse. Они не выключают отскок, а включают его даже тогда, когда контент меньше экрана, и действуют только приbounces == true. Ставить их вfalseради отключения бессмысленно;bouncesVerticallyиbouncesHorizontally: появились в новых версиях UIKit и выключают отскок по одной оси. Без них по одной оси обходились ограничениемcontentOffsetвscrollViewDidScroll, но это даёт подёргивания;bouncesZoom: отдельный отскок при масштабировании за пределыminimumZoomScaleиmaximumZoomScale.
В SwiftUI есть .scrollBounceBehavior(.basedOnSize) с iOS 16.4: отскок остаётся, только когда контент больше области прокрутки.
Скажите, когда отключение оправдано: веб-контент внутри нативного экрана, который должен выглядеть частью интерфейса, нижняя шторка со своей логикой жестов, вложенные прокрутки. В остальных случаях отскок это ожидаемое поведение платформы, по нему пользователь понимает, что дошёл до края.
Где спотыкаются
Ставят alwaysBounceVertical = false и удивляются, что отскок остался. Это свойство отвечает за противоположное.
Выключают отскок и теряют UIRefreshControl. Обновление вытягивается именно перерастягиванием за верхний край, без bounces его не потянуть, а на коротком списке ему, наоборот, нужен alwaysBounceVertical = true.
Отключают отскок глобально через UIScrollView.appearance().bounces = false, чтобы убрать его на одном экране SwiftUI. Это задевает все прокрутки приложения, включая системные контроллеры.