7.5 KiB
TIBottomSheet
TIBottomSheet содержить базовую реализацию модального котроллера и немного видоизмененную библиотеку PanModal.
Базовый контроллер
Для создания модального котроллера можно унаследоваться от BaseModalViewController. Данный клас принимает два generic типа: тип основного контента, тип контента футера.
import TIBottomSheet
import UIKit
class EmptyViewController: BaseModalViewController<UIView, UIView> { }
Обертка вокруг существующего контроллера
Может быть такое, что из уже существующего контроллера нужно сделать модальное окно. С этим может помочь обертка DefaultModalWrapperViewController. Данный контроллер является наследником BaseModalViewController, что позволяет его настраивать так же, как и базовый модальный котроллер
import TIUIKitCore
final class OldMassiveViewController: BaseInitializableViewController {
// some implementation
}
typealias ModalOldMassiveViewController = DefaultModalWrapperViewController<OldMassiveViewController>
class PresentingViewController: BaseInitializableViewController {
// some implementation
@objc private func onButtonTapped() {
presentPanModal(ModalOldMassiveViewController(contentViewController: OldMassiveViewController()))
}
}
Контент модального контроллера
Модальный котроллер может содержать следующие элементы: DragView, HeaderView, FooterView. Каждый из них является опциональным и без дополнительных настроек не будет показываться.
DragView - небольшая view, за которую пользователь "держит" модальный контроллер HeaderView - контейнер, содержащий в себе кнопки назад/закрыть или какие-то другие элементы управления FooterView - view, располагающаяся внизу контроллера, поверх всего контента (модальный контроллер уже настроен так, чтобы при скролле в самый низ, футер не перекрывал последнюю ячейку)
Для настройки каждого у котроллера есть свойство viewControllerAppearance. Через него будет настраиваться весь контроллер. Однако стоит заметить, что котроллер не будет настраивать передаваимую вью, содержащую основной контент. Стандартно котроллер будет пытаться расположить контент так, чтобы он заполнил все пространство.
Вот пример настройки внешнего вида так, чтобы был видет dragView и headerView с левой кнопкой:
import TIUIElements
let customViewController = BaseModalViewController<UIView, UIView>()
customViewController.viewControllerAppearance = BaseModalViewController.DefaultAppearance.make {
$0.dragViewState = .presented(.defaultAppearance)
$0.headerViewState = .presented(.make {
$0.layout.size = .fixedHeight(52)
$0.backgroundColor = .white
$0.contentViewState = .leadingButton(.init(titles: [.normal: "Close"],
appearance: .init(stateAppearances: [
.normal: .init(background: UIViewColorBackground(color: .blue))
])))
})
}
"Якори" контроллера
Раньше для настройки высоты контроллера необходимо было пользоваться свойствами longFormHeight, shortFormHeight. В базовом контроллере можно лишь передать список точек на которых контроллер должен будет задержаться:
let detentsViewController = BaseModalViewController<UIView, UIView>()
detentsViewController.viewControllerAppearance.presentationDetents = [.headerOnly, .height(300), .maxHeight]
- headerOnly будет сам пытаться вычеслить высоту хедера и dragView, показывая только их
- height(_) будет показывать контроллер на переданной высоте
- maxHeight - вся высота экрана (до safeArea)
В данный массив не рекомендуется передавать больше 3 значений, т.к. модальное окно все равно сможет занять только 3 положения на экране.
DimmedView и PassthroughDimmedView
Для контроля DimmedView (затемняющей view) есть отдельное свойство dimmedView. Эти классы позволяют настраивать поведение при тапе в затемнённую область и кастомизировать затемнение под ваши нужды.
let shadowViewController = BaseModalViewController<UIView, UIView>()
let dimmedView = PassthroughDimmedView()
dimmedView.hitTestHandlerView = shadowViewController.view
dimmedView.configureUIView(appearance: UIView.DefaultAppearance(shadow: UIViewShadow(radius: 8,
color: .black,
opacity: 0.3)))
shadowViewController.dimmedView = dimmedView
Контроль закрытия
PanModalPresentable не умеет в настройку закрытия контроллера, делая это самостоятельно через dismiss(animated:completion:). Теперь можно настроить закрытие самостоятельно через свойства: onTapToDismiss и onDragToDismiss.
Взаимодействие с PanModal
Если нет необходимости или возможности использовать BaseModalViewController, вы все так же можете пользоваться протоколом PanModalRepresentable. Вот список изменений протокола:
- Открытие/закрытие модального окна теперь можно настроить с помощью свойств
onTapToDismissиonDragToDismiss - Можно настроить промежуточное состояние модального окна с
mediumFormHeight DimmedViewоткрыт для наследования и может создаваться вdimmedViewу
Для
BaseModalViewControllerвсе свойства изPanModalPresentableвсе также работают, т.е. вы можете их переопределять, добавлять и изменять по необходимости.