Skip to content

Commit

Permalink
Introduce BottomSheet configuration for customizing its appearance
Browse files Browse the repository at this point in the history
  • Loading branch information
mikhailmaslo committed Aug 15, 2022
1 parent 88e5a21 commit 821ac4c
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 33 deletions.
4 changes: 4 additions & 0 deletions BottomSheetDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
7DB24C9327BD18F1001030C7 /* BottomSheetUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DB24C8327BD1813001030C7 /* BottomSheetUtils.framework */; };
7DB24CEC27BD1FAD001030C7 /* JMMulticastDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DB24CE927BD1FAD001030C7 /* JMMulticastDelegate.m */; };
7DD5185028AA2FBA003F3D2A /* UIViewController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DD5184E28AA2F46003F3D2A /* UIViewController+Convenience.swift */; };
7DD5185328AA369E003F3D2A /* BottomSheetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DD5185128AA34D9003F3D2A /* BottomSheetConfiguration.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -111,6 +112,7 @@
7DB24C8327BD1813001030C7 /* BottomSheetUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BottomSheetUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7DB24CE927BD1FAD001030C7 /* JMMulticastDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JMMulticastDelegate.m; sourceTree = "<group>"; };
7DD5184E28AA2F46003F3D2A /* UIViewController+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Convenience.swift"; sourceTree = "<group>"; };
7DD5185128AA34D9003F3D2A /* BottomSheetConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetConfiguration.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -248,6 +250,7 @@
7DD5184D28AA2F3B003F3D2A /* Extensions */,
7DA6E0B5274F915A009F5C37 /* NavigationController */,
7DA6E0B9274F915A009F5C37 /* Presentation */,
7DD5185128AA34D9003F3D2A /* BottomSheetConfiguration.swift */,
);
path = Core;
sourceTree = "<group>";
Expand Down Expand Up @@ -508,6 +511,7 @@
7DA6E0DE274F918E009F5C37 /* BottomSheetModalDismissalHandler.swift in Sources */,
7DA6E0DF274F918E009F5C37 /* BottomSheetPresentationController.swift in Sources */,
7DA6E0E7274F919A009F5C37 /* UIScrollView+MulticastDelegate.swift in Sources */,
7DD5185328AA369E003F3D2A /* BottomSheetConfiguration.swift in Sources */,
7DA6E0E8274F919A009F5C37 /* UINavigationController+MulticastDelegate.swift in Sources */,
7DA6E0E6274F9196009F5C37 /* CGSize+Helpers.swift in Sources */,
7DD5185028AA2FBA003F3D2A /* UIViewController+Convenience.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ final class RootViewController: UIViewController {
@objc
private func handleShowBottomSheet() {
let viewController = ResizeViewController(initialHeight: 300)
let navigationController = BottomSheetNavigationController(rootViewController: viewController)
presentBottomSheet(viewController: navigationController)
presentBottomSheetInsideNavigationController(
viewController: viewController,
configuration: .default
)
}
}
56 changes: 56 additions & 0 deletions Sources/BottomSheet/Core/BottomSheetConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// BottomSheetConfiguration.swift
// BottomSheetDemo
//
// Created by Mikhail Maslo on 15.08.2022.
// Copyright © 2022 Joom. All rights reserved.
//

import UIKit

public struct BottomSheetConfiguration {
public enum PullBarConfiguration {
public struct PullBarAppearance {
public let height: CGFloat

public init(height: CGFloat) {
self.height = height
}
}

case hidden
case visible(PullBarAppearance)

public static let `default`: PullBarConfiguration = .visible(PullBarAppearance(height: 20))
}

public struct ShadowConfiguration {
public let backgroundColor: UIColor

public init(backgroundColor: UIColor) {
self.backgroundColor = backgroundColor
}

public static let `default` = ShadowConfiguration(backgroundColor: UIColor.black.withAlphaComponent(0.6))
}

public let cornerRadius: CGFloat
public let pullBarConfiguration: PullBarConfiguration
public let shadowConfiguration: ShadowConfiguration

public init(
cornerRadius: CGFloat,
pullBarConfiguration: PullBarConfiguration,
shadowConfiguration: ShadowConfiguration
) {
self.cornerRadius = cornerRadius
self.pullBarConfiguration = pullBarConfiguration
self.shadowConfiguration = shadowConfiguration
}

public static let `default` = BottomSheetConfiguration(
cornerRadius: 10,
pullBarConfiguration: .default,
shadowConfiguration: .default
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ public final class DefaultBottomSheetPresentationControllerFactory: BottomSheetP

// MARK: - Public properties

private let configuration: BottomSheetConfiguration
private let dismissalHandlerProvider: DismissalHandlerProvider

// MARK: - Init

public init(dismissalHandlerProvider: @escaping DismissalHandlerProvider) {
public init(
configuration: BottomSheetConfiguration,
dismissalHandlerProvider: @escaping DismissalHandlerProvider
) {
self.dismissalHandlerProvider = dismissalHandlerProvider
self.configuration = configuration
}

// MARK: - BottomSheetPresentationControllerFactory
Expand All @@ -32,7 +37,8 @@ public final class DefaultBottomSheetPresentationControllerFactory: BottomSheetP
BottomSheetPresentationController(
presentedViewController: presentedViewController,
presentingViewController: presentingViewController,
dismissalHandler: dismissalHandlerProvider()
dismissalHandler: dismissalHandlerProvider(),
configuration: configuration
)
}
}
Expand Down Expand Up @@ -70,10 +76,10 @@ public extension UIViewController {

private static var bottomSheetTransitionDelegateKey: UInt8 = 0

func presentBottomSheet(viewController: UIViewController) {
func presentBottomSheet(viewController: UIViewController, configuration: BottomSheetConfiguration) {
weak var presentingViewController = self
weak var currentBottomSheetTransitionDelegate: UIViewControllerTransitioningDelegate?
let presentationControllerFactory = DefaultBottomSheetPresentationControllerFactory {
let presentationControllerFactory = DefaultBottomSheetPresentationControllerFactory(configuration: configuration) {
DefaultBottomSheetModalDismissalHandler(presentingViewController: presentingViewController) {
if currentBottomSheetTransitionDelegate === presentingViewController?.bottomSheetTransitionDelegate {
presentingViewController?.bottomSheetTransitionDelegate = nil
Expand All @@ -88,4 +94,9 @@ public extension UIViewController {
viewController.modalPresentationStyle = .custom
present(viewController, animated: true, completion: nil)
}

func presentBottomSheetInsideNavigationController(viewController: UIViewController, configuration: BottomSheetConfiguration) {
let navigationController = BottomSheetNavigationController(rootViewController: viewController, configuration: configuration)
presentBottomSheet(viewController: navigationController, configuration: configuration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,20 @@ public final class BottomSheetNavigationController: UINavigationController {
private var canAnimatePreferredContentSizeUpdates = false

private weak var lastTransitionViewController: UIViewController?


private let configuration: BottomSheetConfiguration

// MARK: - Init

public init(rootViewController: UIViewController, configuration: BottomSheetConfiguration) {
self.configuration = configuration
super.init(rootViewController: rootViewController)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - UIViewController

public override func viewDidLoad() {
Expand Down Expand Up @@ -128,7 +141,7 @@ extension BottomSheetNavigationController: UINavigationControllerDelegate {
}

lastTransitionViewController = fromVC
return BottomSheetNavigationAnimatedTransitioning(operation: operation)
return BottomSheetNavigationAnimatedTransitioning(operation: operation, configuration: configuration)
}

public func navigationController(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ public final class BottomSheetNavigationAnimatedTransitioning: NSObject, UIViewC
// MARK: - Private

private let operation: UINavigationController.Operation
private let configuration: BottomSheetConfiguration

// MARK: - Init

public init(operation: UINavigationController.Operation) {
public init(
operation: UINavigationController.Operation,
configuration: BottomSheetConfiguration
) {
self.operation = operation
self.configuration = configuration
}

// MARK: - UIViewControllerAnimatedTransitioning
Expand Down Expand Up @@ -78,9 +83,10 @@ public final class BottomSheetNavigationAnimatedTransitioning: NSObject, UIViewC
height: destinationViewController.preferredContentSize.height + destinationView.safeAreaInsets.top + destinationView.safeAreaInsets.bottom
)

let maxHeight = containerViewWindow.bounds.size.height
- containerViewWindow.safeAreaInsets.top
- BottomSheetPresentationController.pullBarHeight
var maxHeight = containerViewWindow.bounds.size.height - containerViewWindow.safeAreaInsets.top
if case .visible(let appearance) = configuration.pullBarConfiguration {
maxHeight -= appearance.height
}

let targetSize = CGSize(
width: preferredContentSize.width,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,8 @@ public final class BottomSheetPresentationController: UIPresentationController {
case dismissing
}

private struct Style {
static let cornerRadius: CGFloat = 10
static let pullBarHeight = Style.cornerRadius * 2
}

// MARK: - Public properties

static var pullBarHeight: CGFloat {
Style.pullBarHeight
}

var interactiveTransition: UIViewControllerInteractiveTransitioning? {
interactionController
}
Expand Down Expand Up @@ -75,15 +66,18 @@ public final class BottomSheetPresentationController: UIPresentationController {
private var cachedInsets: UIEdgeInsets = .zero

private let dismissalHandler: BottomSheetModalDismissalHandler
private let configuration: BottomSheetConfiguration

// MARK: - Init

public init(
presentedViewController: UIViewController,
presentingViewController: UIViewController?,
dismissalHandler: BottomSheetModalDismissalHandler
dismissalHandler: BottomSheetModalDismissalHandler,
configuration: BottomSheetConfiguration
) {
self.dismissalHandler = dismissalHandler
self.configuration = configuration
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
}

Expand Down Expand Up @@ -266,7 +260,7 @@ public final class BottomSheetPresentationController: UIPresentationController {
presentedViewController.view.clipsToBounds = true

pullBar?.layer.mask = nil
presentedViewController.view.layer.cornerRadius = Style.cornerRadius
presentedViewController.view.layer.cornerRadius = configuration.cornerRadius
presentedViewController.view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}

Expand All @@ -276,22 +270,31 @@ public final class BottomSheetPresentationController: UIPresentationController {
return
}

let shadingView = UIView()
shadingView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
containerView.addSubview(shadingView)
shadingView.frame = containerView.bounds
addShadow(containerView: containerView)
addPullBarIfNeeded(containerView: containerView)
}

private func addPullBarIfNeeded(containerView: UIView) {
guard case .visible(let appearance) = configuration.pullBarConfiguration else { return }
let pullBar = PullBar()
pullBar.frame.size = CGSize(width: containerView.frame.width, height: Style.pullBarHeight)
pullBar.frame.size = CGSize(width: containerView.frame.width, height: appearance.height)
containerView.addSubview(pullBar)

self.pullBar = pullBar
}

private func addShadow(containerView: UIView) {
let shadingView = UIView()
shadingView.backgroundColor = configuration.shadowConfiguration.backgroundColor
containerView.addSubview(shadingView)
shadingView.frame = containerView.bounds

let tapGesture = UITapGestureRecognizer()
shadingView.addGestureRecognizer(tapGesture)

tapGesture.addTarget(self, action: #selector(handleShadingViewTapGesture))

self.shadingView = shadingView
self.pullBar = pullBar
}

@objc
Expand All @@ -314,7 +317,10 @@ public final class BottomSheetPresentationController: UIPresentationController {
let windowInsets = presentedView?.window?.safeAreaInsets ?? cachedInsets

let preferredHeight = presentedViewController.preferredContentSize.height + windowInsets.bottom
let maxHeight = containerView.bounds.height - windowInsets.top - Style.pullBarHeight
var maxHeight = containerView.bounds.height - windowInsets.top
if case .visible(let appearance) = configuration.pullBarConfiguration {
maxHeight -= appearance.height
}
let height = min(preferredHeight, maxHeight)

return .init(
Expand All @@ -334,7 +340,9 @@ public final class BottomSheetPresentationController: UIPresentationController {
let targetFrame = targetFrameForPresentedView()
if !oldFrame.isAlmostEqual(to: targetFrame) {
presentedView.frame = targetFrame
pullBar?.frame.origin.y = presentedView.frame.minY - Style.pullBarHeight + pixelSize
if case .visible(let appearance) = configuration.pullBarConfiguration {
pullBar?.frame.origin.y = presentedView.frame.minY - appearance.height + pixelSize
}
}
}

Expand Down Expand Up @@ -520,15 +528,21 @@ extension BottomSheetPresentationController: UIViewControllerAnimatedTransitioni
size: sourceView.frame.size
)

let updatePullBarFrame = {
guard case .visible(let appearnce) = self.configuration.pullBarConfiguration else { return }

self.pullBar?.frame.origin.y = presentedView.frame.minY - appearnce.height + pixelSize
}

presentedView.frame = isPresenting ? offscreenFrame : frameInContainer
pullBar?.frame.origin.y = presentedView.frame.minY - Style.pullBarHeight + pixelSize
updatePullBarFrame()
shadingView?.alpha = isPresenting ? 0 : 1

applyStyle()

let animations = {
presentedView.frame = isPresenting ? frameInContainer : offscreenFrame
self.pullBar?.frame.origin.y = presentedView.frame.minY - Style.pullBarHeight + pixelSize
updatePullBarFrame()
self.shadingView?.alpha = isPresenting ? 1 : 0
}

Expand Down

0 comments on commit 821ac4c

Please sign in to comment.