├── .gitignore ├── .travis.yml ├── DynamicBottomSheet.podspec ├── DynamicBottomSheet ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── DynamicBottomSheetPresentationController.swift │ └── DynamicBottomSheetViewController.swift ├── Example ├── DynamicBottomSheet.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── DynamicBottomSheet-Example.xcscheme ├── DynamicBottomSheet.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── DynamicBottomSheet │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── LabelViewController.swift │ ├── MyBottomSheetViewController.swift │ ├── MyStackViewBottomSheetViewController.swift │ ├── MyViewController.swift │ └── ViewController.swift ├── Podfile ├── Podfile.lock └── Tests │ ├── Info.plist │ └── Tests.swift ├── LICENSE ├── Package.swift ├── README.md ├── _Pods.xcodeproj └── resources └── preview.gif /.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | 3 | # macOS 4 | .DS_Store 5 | 6 | # Xcode 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata/ 17 | *.xccheckout 18 | profile 19 | *.moved-aside 20 | DerivedData 21 | *.hmap 22 | *.ipa 23 | 24 | # Bundler 25 | .bundle 26 | 27 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 28 | # Carthage/Checkouts 29 | 30 | Carthage/Build 31 | 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 35 | # 36 | # Note: if you ignore the Pods directory, make sure to uncomment 37 | # `pod install` in .travis.yml 38 | # 39 | # Pods/ 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/DynamicBottomSheet.xcworkspace -scheme DynamicBottomSheet-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /DynamicBottomSheet.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint DynamicBottomSheet.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'DynamicBottomSheet' 11 | s.version = '1.0.2' 12 | s.summary = 'Customizable Dynamic Bottom Sheet Library for iOS' 13 | 14 | s.description = <<-DESC 15 | 16 | Fully Customizable Dynamic Bottom Sheet Library for iOS. 17 | This library doesn't support storyboards. 18 | However, you can easily override variables in DynamicBottomSheetViewController and make the bottom sheet programmatically. 19 | 20 | DESC 21 | 22 | s.homepage = 'https://github.com/aaronLab/DynamicBottomSheet' 23 | s.license = { :type => 'MIT', :file => 'LICENSE' } 24 | s.author = { 'Aaron Lee' => 'aaronlab.net@gmail.com' } 25 | s.source = { :git => 'https://github.com/aaronLab/DynamicBottomSheet.git', :tag => s.version.to_s } 26 | 27 | s.requires_arc = true 28 | 29 | s.swift_version = "5.0" 30 | s.ios.deployment_target = '10.0' 31 | 32 | s.source_files = 'DynamicBottomSheet/Classes/*.{swift}' 33 | 34 | s.dependency 'RxSwift', '~> 6.0' 35 | s.dependency 'RxCocoa', '~> 6.0' 36 | s.dependency 'RxGesture', '~> 4.0' 37 | s.dependency 'SnapKit', '~> 5.0' 38 | s.dependency 'Then', '~> 2.0' 39 | 40 | end 41 | -------------------------------------------------------------------------------- /DynamicBottomSheet/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronlab/DynamicBottomSheet/25595d2314e8c2648feeb0da690748ef1a914889/DynamicBottomSheet/Assets/.gitkeep -------------------------------------------------------------------------------- /DynamicBottomSheet/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronlab/DynamicBottomSheet/25595d2314e8c2648feeb0da690748ef1a914889/DynamicBottomSheet/Classes/.gitkeep -------------------------------------------------------------------------------- /DynamicBottomSheet/Classes/DynamicBottomSheetPresentationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicBottomSheetPresentationController.swift 3 | // DynamicBottomSheet 4 | // 5 | // Created by Aaron Lee on 2021/11/08. 6 | // 7 | 8 | import UIKit 9 | 10 | /// DynamicBottomSheetPresentationController 11 | open class DynamicBottomSheetPresentationController: UIPresentationController { 12 | 13 | /// Background View 14 | public var backgroundView = UIView() 15 | 16 | // MARK: - Init 17 | 18 | public override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) { 19 | super.init(presentedViewController: presentedViewController, presenting: presentingViewController) 20 | 21 | configureBackgroundView() 22 | } 23 | 24 | /// Configure the background view 25 | private func configureBackgroundView() { 26 | backgroundView.frame = CGRect(origin: .zero, size: UIScreen.main.bounds.size) 27 | backgroundView.alpha = 0 28 | } 29 | 30 | open override func presentationTransitionWillBegin() { 31 | containerView?.insertSubview(backgroundView, at: 0) 32 | } 33 | 34 | open override func dismissalTransitionWillBegin() { 35 | presentedViewController.transitionCoordinator?.animate(alongsideTransition: { _ in 36 | self.backgroundView.alpha = 0 37 | }, completion: nil) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /DynamicBottomSheet/Classes/DynamicBottomSheetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicBottomSheetViewController.swift 3 | // DynamicBottomSheetViewController 4 | // 5 | // Created by Aaron Lee on 2021/11/05. 6 | // 7 | 8 | import UIKit 9 | import RxSwift 10 | import RxCocoa 11 | import RxGesture 12 | import SnapKit 13 | import Then 14 | 15 | /// Dynamic Bottom Sheet View Controller 16 | /// 17 | /// You can easily make a bottom-sheet-styled view controller. 18 | /// 19 | /// You can also `override` or `set` a bunch of parameters to design your own bottom sheet view controller, 20 | /// and see their default values respectively when you `option + left` click on the parameters. 21 | /// - `open var backgroundColor: UIColor` 22 | /// - `open var backgroundView: UIView` 23 | /// - `open var contentViewBackgroundColor: UIColor` 24 | /// - `open var height: CGFloat?` 25 | /// - `open var contentView: UIView` 26 | /// - `open var contentViewCornerRadius: CGFloat` 27 | /// - `open var transitionDuration: CGFloat` 28 | /// - `open var dismissVelocityThreshold: CGFloat` 29 | open class DynamicBottomSheetViewController: UIViewController { 30 | 31 | // MARK: - Public Properties 32 | 33 | /// The background color of the UIPresentationController. 34 | /// 35 | /// - `UIColor.black.withAlphaComponent(0.6)` for others. 36 | open var backgroundColor: UIColor = { 37 | return .black.withAlphaComponent(0.6) 38 | }() 39 | 40 | /// Background view 41 | open var backgroundView = UIView() 42 | 43 | /// The background color of the content view. 44 | /// 45 | /// Default value 46 | /// - `UIColor.tertiarySystemBackground` for iOS 13 or later. 47 | /// - `UIColor.white` for others. 48 | open var contentViewBackgroundColor: UIColor = { 49 | if #available(iOS 13, *) { 50 | return .tertiarySystemBackground 51 | } 52 | 53 | return .white 54 | }() 55 | 56 | private var _height: CGFloat? = nil 57 | /// The height of the content view. 58 | /// 59 | /// Default value is `nil` 60 | /// 61 | /// If you set this value explicitly, the height of the content view will be fixed. 62 | open var height: CGFloat? { 63 | get { 64 | return _height 65 | } 66 | set { 67 | _height = newValue 68 | } 69 | } 70 | 71 | /// Content view 72 | open var contentView: UIView = UIView() 73 | 74 | /// Corner radius of the content view(top left, top right) 75 | /// 76 | /// Default value is `16.0` 77 | open var contentViewCornerRadius: CGFloat = 16 78 | 79 | /// Present / Dismiss transition duration 80 | /// 81 | /// Default value is 0.3 82 | open var transitionDuration: CGFloat = 0.3 83 | 84 | /// Dismiss velocity threshold 85 | /// 86 | /// Default value is 500 87 | open var dismissVelocityThreshold: CGFloat = 500 88 | 89 | // MARK: - Private Properties 90 | 91 | /// Dispose bag for Rx 92 | private var bag = DisposeBag() 93 | 94 | /// The origin vertical centre of the content view 95 | private var originCentreY: CGFloat = .zero 96 | 97 | // MARK: - Init 98 | 99 | /// Initialize with parameters below. 100 | /// 101 | /// Default values 102 | /// 103 | /// - `modalPresentationStyle = .overFullScreen` 104 | /// - `modalTransitionStyle = .crossDissolve` 105 | public init() { 106 | super.init(nibName: nil, bundle: nil) 107 | 108 | initPresentationStyle() 109 | } 110 | 111 | /// Initialize with parameters below. 112 | /// 113 | /// Default values 114 | /// 115 | /// - `modalPresentationStyle = .custom` 116 | /// - `modalTransitionStyle = .coverVertical` 117 | public init(modalPresentationStyle: UIModalPresentationStyle = .custom, 118 | modalTransitionStyle: UIModalTransitionStyle = .coverVertical) { 119 | super.init(nibName: nil, bundle: nil) 120 | 121 | initPresentationStyle(modalPresentationStyle: modalPresentationStyle, 122 | modalTransitionStyle: modalTransitionStyle) 123 | } 124 | 125 | public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 126 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 127 | 128 | initPresentationStyle() 129 | } 130 | 131 | required public init?(coder: NSCoder) { 132 | super.init(coder: coder) 133 | 134 | initPresentationStyle() 135 | } 136 | 137 | /// This method defines of two initial values of the view controller 138 | /// 139 | /// Default values 140 | /// 141 | /// - `modalPresentationStyle = .custom` 142 | /// - `modalTransitionStyle = .coverVertical` 143 | open func initPresentationStyle(modalPresentationStyle: UIModalPresentationStyle = .custom, 144 | modalTransitionStyle: UIModalTransitionStyle = .coverVertical) { 145 | self.modalPresentationStyle = modalPresentationStyle 146 | self.modalTransitionStyle = modalTransitionStyle 147 | transitioningDelegate = self 148 | } 149 | 150 | // MARK: - Lifecycle 151 | 152 | open override func viewDidLayoutSubviews() { 153 | super.viewDidLayoutSubviews() 154 | 155 | curveTopCorners() 156 | } 157 | 158 | open override func viewDidLoad() { 159 | super.viewDidLoad() 160 | 161 | configureView() 162 | bindRx() 163 | } 164 | 165 | open override func viewWillAppear(_ animated: Bool) { 166 | super.viewWillAppear(animated) 167 | 168 | DispatchQueue.main.async { 169 | self.showBottomSheet() 170 | } 171 | } 172 | 173 | // MARK: - Helper 174 | 175 | /// This method shows the bottom sheet animation on `viewWillAppear` 176 | open func showBottomSheet() { 177 | originCentreY = contentView.center.y 178 | 179 | let contentViewHeight = contentView.frame.height 180 | contentView.center.y += contentViewHeight 181 | 182 | UIView.animate(withDuration: transitionDuration) { 183 | self.contentView.center.y -= contentViewHeight 184 | } 185 | } 186 | 187 | /// This method makes the content view curved. 188 | /// 189 | /// This is called on `viewDidLayoutSubviews` 190 | open func curveTopCorners() { 191 | let size = CGSize(width: contentViewCornerRadius, height: .zero) 192 | 193 | let path = UIBezierPath(roundedRect: contentView.bounds, 194 | byRoundingCorners: [.topLeft, .topRight], 195 | cornerRadii: size) 196 | 197 | let maskLayer = CAShapeLayer() 198 | maskLayer.frame = contentView.bounds 199 | maskLayer.path = path.cgPath 200 | contentView.layer.mask = maskLayer 201 | } 202 | 203 | } 204 | 205 | // MARK: - Layout 206 | 207 | extension DynamicBottomSheetViewController { 208 | 209 | /// This method configures the whole view. 210 | @objc 211 | open func configureView() { 212 | view.backgroundColor = .clear 213 | 214 | layoutBackgroundView() 215 | layoutContentView() 216 | } 217 | 218 | /// This method layouts the background view. 219 | @objc 220 | open func layoutBackgroundView() { 221 | view.addSubview(backgroundView) 222 | backgroundView.snp.makeConstraints { 223 | $0.edges.equalToSuperview() 224 | } 225 | } 226 | 227 | /// This method layouts the content view. 228 | /// 229 | /// If the `height` property of the view controller is not `nil`, the height of the content view will be fixed. 230 | @objc 231 | open func layoutContentView() { 232 | contentView.backgroundColor = contentViewBackgroundColor 233 | 234 | view.addSubview(contentView) 235 | contentView.snp.makeConstraints { 236 | $0.leading.trailing.bottom.equalToSuperview() 237 | $0.top.lessThanOrEqualToSuperview().priority(.low) 238 | } 239 | 240 | // Fixd height 241 | if let height = height { 242 | contentView.snp.makeConstraints { 243 | $0.height.equalTo(height) 244 | } 245 | } 246 | } 247 | 248 | } 249 | 250 | // MARK: - Bind 251 | 252 | extension DynamicBottomSheetViewController { 253 | 254 | /// Bind Rx 255 | private func bindRx() { 256 | bindBackgroundViewTapGesture() 257 | bindContentViewCentre() 258 | bindContentViewPanGesture() 259 | } 260 | 261 | /// Bind the tap gesture on the background view. 262 | private func bindBackgroundViewTapGesture() { 263 | backgroundView 264 | .rx 265 | .gesture(.tap(configuration: { _, delegate in 266 | delegate.simultaneousRecognitionPolicy = .never 267 | })) 268 | .when(.recognized) 269 | .bind { [weak self] gesture in 270 | DispatchQueue.main.async { 271 | self?.shouldDismissSheet() 272 | } 273 | } 274 | .disposed(by: bag) 275 | } 276 | 277 | /// This method will dismiss the bottom sheet. 278 | /// 279 | /// This is called when you tap the background view or swipe down on the content view. 280 | @objc 281 | open func shouldDismissSheet() { 282 | let contentViewHeight = contentView.frame.height 283 | 284 | UIView.animate(withDuration: transitionDuration) { 285 | self.contentView.center.y += contentViewHeight 286 | } completion: { _ in 287 | self.dismiss(animated: false) 288 | } 289 | 290 | } 291 | 292 | /// Bind the centre of the content view 293 | private func bindContentViewCentre() { 294 | contentView 295 | .rx 296 | .observe(CGPoint.self, #keyPath(UIView.center)) 297 | .asDriver(onErrorJustReturn: .zero) 298 | .drive(onNext: { [weak self] centre in 299 | guard let self = self else { return } 300 | 301 | let ratio = (self.contentView.center.y - self.originCentreY) / self.originCentreY 302 | let alpha = 1 - ratio 303 | 304 | self.dimBackgroundView(alpha) 305 | }) 306 | .disposed(by: bag) 307 | } 308 | 309 | /// This changes the alpha of the background of the UIPresentationController. 310 | /// 311 | /// 312 | /// - Parameter proportion: The alpha value of the background view. 313 | @objc 314 | open func dimBackgroundView(_ proportion: CGFloat) { 315 | guard let presentationController = presentationController as? DynamicBottomSheetPresentationController else { return } 316 | UIView.animate(withDuration: transitionDuration) { 317 | presentationController.backgroundView.alpha = proportion 318 | } 319 | } 320 | 321 | /// Bind the pan gesture on the content view. 322 | private func bindContentViewPanGesture() { 323 | contentView 324 | .rx 325 | .gesture(.pan(configuration: nil)) 326 | .bind { [weak self] gesture in 327 | guard let self = self, 328 | let gesture = gesture as? UIPanGestureRecognizer else { return } 329 | 330 | self.contentViewDidPan(gesture, in: self.contentView) 331 | } 332 | .disposed(by: bag) 333 | } 334 | 335 | /// When the content view is panned. 336 | /// 337 | /// This method is called when you pan the content view. 338 | @objc 339 | open func contentViewDidPan(_ gesture: UIPanGestureRecognizer, in view: UIView) { 340 | 341 | if gesture.state == .changed { 342 | contentViewPanGestureDidChange(gesture, in: view) 343 | } 344 | 345 | if gesture.state == .ended { 346 | contentViewPanGestureDidEnd(gesture, in: view) 347 | } 348 | } 349 | 350 | /// This method is called when the pan gesture on the content view is detected. 351 | @objc 352 | open func contentViewPanGestureDidChange(_ gesture: UIPanGestureRecognizer, in view: UIView) { 353 | guard gesture.view != nil else { return } 354 | 355 | let translation = gesture.translation(in: view) 356 | let translatedY = gesture.view!.center.y + translation.y 357 | 358 | if translatedY < originCentreY { 359 | 360 | if gesture.view!.center.y > originCentreY { 361 | gesture.view!.center.y = originCentreY 362 | } 363 | 364 | return 365 | } 366 | 367 | gesture.view!.center = CGPoint(x: gesture.view!.center.x, y: translatedY) 368 | 369 | gesture.setTranslation(.zero, in: view) 370 | } 371 | 372 | /// This method is called when the pan gesture is ended. 373 | @objc 374 | open func contentViewPanGestureDidEnd(_ gesture: UIPanGestureRecognizer, in view: UIView) { 375 | guard shouldDismiss(gesture, in: view, threshold: dismissVelocityThreshold) else { 376 | 377 | DispatchQueue.main.async { 378 | self.shouldRestoreSheet() 379 | } 380 | 381 | return 382 | } 383 | 384 | DispatchQueue.main.async { 385 | self.shouldDismissSheet() 386 | } 387 | } 388 | 389 | /// This method restores the first position of the content view. 390 | @objc 391 | open func shouldRestoreSheet() { 392 | UIView.animate(withDuration: transitionDuration) { 393 | self.contentView.center.y = self.originCentreY 394 | } 395 | 396 | } 397 | 398 | /// This method defines if the bottom sheet should be dismissed or not. 399 | @objc 400 | open func shouldDismiss(_ gesture: UIPanGestureRecognizer, in view: UIView, threshold: CGFloat) -> Bool { 401 | let verticalVelocity = verticalVelocity(gesture, in: view) 402 | let movedDownHalf = view.frame.minY >= originCentreY 403 | 404 | return verticalVelocity > dismissVelocityThreshold || movedDownHalf 405 | } 406 | 407 | /// This method returns the vertical velocity of the pan gesture in the content view. 408 | @objc 409 | open func verticalVelocity(_ gesture: UIPanGestureRecognizer, in view: UIView) -> CGFloat { 410 | let velocity = gesture.velocity(in: view) 411 | 412 | return velocity.y 413 | } 414 | 415 | } 416 | 417 | // MARK: - UIViewControllerTransitioningDelegate 418 | 419 | extension DynamicBottomSheetViewController: UIViewControllerTransitioningDelegate { 420 | 421 | public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { 422 | let presentationController = DynamicBottomSheetPresentationController(presentedViewController: presented, presenting: presenting) 423 | presentationController.backgroundView.backgroundColor = backgroundColor 424 | 425 | return presentationController 426 | } 427 | 428 | } 429 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 11 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 12 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 13 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 14 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 15 | 775E8A3F7FE26FC072FAF11B /* Pods_DynamicBottomSheet_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35A7FBA31802E24FCCC6CC34 /* Pods_DynamicBottomSheet_Example.framework */; }; 16 | A40A984B27379A89009CAE9F /* MyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40A984A27379A89009CAE9F /* MyViewController.swift */; }; 17 | A40A984D27379AA2009CAE9F /* LabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40A984C27379AA2009CAE9F /* LabelViewController.swift */; }; 18 | A40A984F27379AC3009CAE9F /* MyBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40A984E27379AC3009CAE9F /* MyBottomSheetViewController.swift */; }; 19 | A40A985127379E3F009CAE9F /* MyStackViewBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40A985027379E3F009CAE9F /* MyStackViewBottomSheetViewController.swift */; }; 20 | B14C36BF1DE6DEFD24A2EAA1 /* Pods_DynamicBottomSheet_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25889B10EA9EDF256328FDEF /* Pods_DynamicBottomSheet_Tests.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 29 | remoteInfo = DynamicBottomSheet; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 0581CBD60C17E25C084E9FC6 /* Pods-DynamicBottomSheet_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DynamicBottomSheet_Example.release.xcconfig"; path = "Target Support Files/Pods-DynamicBottomSheet_Example/Pods-DynamicBottomSheet_Example.release.xcconfig"; sourceTree = ""; }; 35 | 25889B10EA9EDF256328FDEF /* Pods_DynamicBottomSheet_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DynamicBottomSheet_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 35A7FBA31802E24FCCC6CC34 /* Pods_DynamicBottomSheet_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DynamicBottomSheet_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 37245365F7D8F2010114ADD5 /* Pods-DynamicBottomSheet_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DynamicBottomSheet_Example.debug.xcconfig"; path = "Target Support Files/Pods-DynamicBottomSheet_Example/Pods-DynamicBottomSheet_Example.debug.xcconfig"; sourceTree = ""; }; 38 | 438D2A2DC9D0E50265EB29AF /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 39 | 607FACD01AFB9204008FA782 /* DynamicBottomSheet_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DynamicBottomSheet_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 43 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 44 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 45 | 607FACE51AFB9204008FA782 /* DynamicBottomSheet_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DynamicBottomSheet_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 48 | 94D6788AB6CAE9E8D0BFF32D /* Pods-DynamicBottomSheet_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DynamicBottomSheet_Tests.release.xcconfig"; path = "Target Support Files/Pods-DynamicBottomSheet_Tests/Pods-DynamicBottomSheet_Tests.release.xcconfig"; sourceTree = ""; }; 49 | A40A984A27379A89009CAE9F /* MyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyViewController.swift; sourceTree = ""; }; 50 | A40A984C27379AA2009CAE9F /* LabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewController.swift; sourceTree = ""; }; 51 | A40A984E27379AC3009CAE9F /* MyBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyBottomSheetViewController.swift; sourceTree = ""; }; 52 | A40A985027379E3F009CAE9F /* MyStackViewBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyStackViewBottomSheetViewController.swift; sourceTree = ""; }; 53 | C1ADAB7C1B5871E3212F4565 /* DynamicBottomSheet.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = DynamicBottomSheet.podspec; path = ../DynamicBottomSheet.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 54 | D9EC73C62611988AE27F1E65 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 55 | FCF403A1528D553F4BF065D4 /* Pods-DynamicBottomSheet_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DynamicBottomSheet_Tests.debug.xcconfig"; path = "Target Support Files/Pods-DynamicBottomSheet_Tests/Pods-DynamicBottomSheet_Tests.debug.xcconfig"; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 775E8A3F7FE26FC072FAF11B /* Pods_DynamicBottomSheet_Example.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | B14C36BF1DE6DEFD24A2EAA1 /* Pods_DynamicBottomSheet_Tests.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 607FACC71AFB9204008FA782 = { 79 | isa = PBXGroup; 80 | children = ( 81 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 82 | 607FACD21AFB9204008FA782 /* Example for DynamicBottomSheet */, 83 | 607FACE81AFB9204008FA782 /* Tests */, 84 | 607FACD11AFB9204008FA782 /* Products */, 85 | B1EFE23BBE52A07A980B95DF /* Pods */, 86 | FCC95784BE727695D61F5CA4 /* Frameworks */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 607FACD11AFB9204008FA782 /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 607FACD01AFB9204008FA782 /* DynamicBottomSheet_Example.app */, 94 | 607FACE51AFB9204008FA782 /* DynamicBottomSheet_Tests.xctest */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | 607FACD21AFB9204008FA782 /* Example for DynamicBottomSheet */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 103 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 104 | A40A984E27379AC3009CAE9F /* MyBottomSheetViewController.swift */, 105 | A40A984A27379A89009CAE9F /* MyViewController.swift */, 106 | A40A984C27379AA2009CAE9F /* LabelViewController.swift */, 107 | A40A985027379E3F009CAE9F /* MyStackViewBottomSheetViewController.swift */, 108 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 109 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 110 | 607FACD31AFB9204008FA782 /* Supporting Files */, 111 | ); 112 | name = "Example for DynamicBottomSheet"; 113 | path = DynamicBottomSheet; 114 | sourceTree = ""; 115 | }; 116 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 607FACD41AFB9204008FA782 /* Info.plist */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 607FACE81AFB9204008FA782 /* Tests */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 128 | 607FACE91AFB9204008FA782 /* Supporting Files */, 129 | ); 130 | path = Tests; 131 | sourceTree = ""; 132 | }; 133 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 607FACEA1AFB9204008FA782 /* Info.plist */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | C1ADAB7C1B5871E3212F4565 /* DynamicBottomSheet.podspec */, 145 | D9EC73C62611988AE27F1E65 /* README.md */, 146 | 438D2A2DC9D0E50265EB29AF /* LICENSE */, 147 | ); 148 | name = "Podspec Metadata"; 149 | sourceTree = ""; 150 | }; 151 | B1EFE23BBE52A07A980B95DF /* Pods */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 37245365F7D8F2010114ADD5 /* Pods-DynamicBottomSheet_Example.debug.xcconfig */, 155 | 0581CBD60C17E25C084E9FC6 /* Pods-DynamicBottomSheet_Example.release.xcconfig */, 156 | FCF403A1528D553F4BF065D4 /* Pods-DynamicBottomSheet_Tests.debug.xcconfig */, 157 | 94D6788AB6CAE9E8D0BFF32D /* Pods-DynamicBottomSheet_Tests.release.xcconfig */, 158 | ); 159 | path = Pods; 160 | sourceTree = ""; 161 | }; 162 | FCC95784BE727695D61F5CA4 /* Frameworks */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 35A7FBA31802E24FCCC6CC34 /* Pods_DynamicBottomSheet_Example.framework */, 166 | 25889B10EA9EDF256328FDEF /* Pods_DynamicBottomSheet_Tests.framework */, 167 | ); 168 | name = Frameworks; 169 | sourceTree = ""; 170 | }; 171 | /* End PBXGroup section */ 172 | 173 | /* Begin PBXNativeTarget section */ 174 | 607FACCF1AFB9204008FA782 /* DynamicBottomSheet_Example */ = { 175 | isa = PBXNativeTarget; 176 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DynamicBottomSheet_Example" */; 177 | buildPhases = ( 178 | D8A2CDED63045B3588F59AD5 /* [CP] Check Pods Manifest.lock */, 179 | 607FACCC1AFB9204008FA782 /* Sources */, 180 | 607FACCD1AFB9204008FA782 /* Frameworks */, 181 | 607FACCE1AFB9204008FA782 /* Resources */, 182 | 5BE0389F54ADF8872EACC52D /* [CP] Embed Pods Frameworks */, 183 | ); 184 | buildRules = ( 185 | ); 186 | dependencies = ( 187 | ); 188 | name = DynamicBottomSheet_Example; 189 | productName = DynamicBottomSheet; 190 | productReference = 607FACD01AFB9204008FA782 /* DynamicBottomSheet_Example.app */; 191 | productType = "com.apple.product-type.application"; 192 | }; 193 | 607FACE41AFB9204008FA782 /* DynamicBottomSheet_Tests */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DynamicBottomSheet_Tests" */; 196 | buildPhases = ( 197 | 4E2BDE74EAFAB8F8F3499C61 /* [CP] Check Pods Manifest.lock */, 198 | 607FACE11AFB9204008FA782 /* Sources */, 199 | 607FACE21AFB9204008FA782 /* Frameworks */, 200 | 607FACE31AFB9204008FA782 /* Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 206 | ); 207 | name = DynamicBottomSheet_Tests; 208 | productName = Tests; 209 | productReference = 607FACE51AFB9204008FA782 /* DynamicBottomSheet_Tests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | 607FACC81AFB9204008FA782 /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | LastSwiftUpdateCheck = 0830; 219 | LastUpgradeCheck = 1300; 220 | ORGANIZATIONNAME = CocoaPods; 221 | TargetAttributes = { 222 | 607FACCF1AFB9204008FA782 = { 223 | CreatedOnToolsVersion = 6.3.1; 224 | LastSwiftMigration = 1300; 225 | }; 226 | 607FACE41AFB9204008FA782 = { 227 | CreatedOnToolsVersion = 6.3.1; 228 | LastSwiftMigration = 1300; 229 | TestTargetID = 607FACCF1AFB9204008FA782; 230 | }; 231 | }; 232 | }; 233 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DynamicBottomSheet" */; 234 | compatibilityVersion = "Xcode 3.2"; 235 | developmentRegion = English; 236 | hasScannedForEncodings = 0; 237 | knownRegions = ( 238 | English, 239 | en, 240 | Base, 241 | ); 242 | mainGroup = 607FACC71AFB9204008FA782; 243 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 244 | projectDirPath = ""; 245 | projectRoot = ""; 246 | targets = ( 247 | 607FACCF1AFB9204008FA782 /* DynamicBottomSheet_Example */, 248 | 607FACE41AFB9204008FA782 /* DynamicBottomSheet_Tests */, 249 | ); 250 | }; 251 | /* End PBXProject section */ 252 | 253 | /* Begin PBXResourcesBuildPhase section */ 254 | 607FACCE1AFB9204008FA782 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 259 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | 607FACE31AFB9204008FA782 /* Resources */ = { 264 | isa = PBXResourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | runOnlyForDeploymentPostprocessing = 0; 269 | }; 270 | /* End PBXResourcesBuildPhase section */ 271 | 272 | /* Begin PBXShellScriptBuildPhase section */ 273 | 4E2BDE74EAFAB8F8F3499C61 /* [CP] Check Pods Manifest.lock */ = { 274 | isa = PBXShellScriptBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | ); 278 | inputFileListPaths = ( 279 | ); 280 | inputPaths = ( 281 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 282 | "${PODS_ROOT}/Manifest.lock", 283 | ); 284 | name = "[CP] Check Pods Manifest.lock"; 285 | outputFileListPaths = ( 286 | ); 287 | outputPaths = ( 288 | "$(DERIVED_FILE_DIR)/Pods-DynamicBottomSheet_Tests-checkManifestLockResult.txt", 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | shellPath = /bin/sh; 292 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 293 | showEnvVarsInLog = 0; 294 | }; 295 | 5BE0389F54ADF8872EACC52D /* [CP] Embed Pods Frameworks */ = { 296 | isa = PBXShellScriptBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | inputPaths = ( 301 | "${PODS_ROOT}/Target Support Files/Pods-DynamicBottomSheet_Example/Pods-DynamicBottomSheet_Example-frameworks.sh", 302 | "${BUILT_PRODUCTS_DIR}/DynamicBottomSheet/DynamicBottomSheet.framework", 303 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework", 304 | "${BUILT_PRODUCTS_DIR}/RxGesture/RxGesture.framework", 305 | "${BUILT_PRODUCTS_DIR}/RxRelay/RxRelay.framework", 306 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", 307 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", 308 | "${BUILT_PRODUCTS_DIR}/Then/Then.framework", 309 | ); 310 | name = "[CP] Embed Pods Frameworks"; 311 | outputPaths = ( 312 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DynamicBottomSheet.framework", 313 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 314 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxGesture.framework", 315 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxRelay.framework", 316 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 317 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", 318 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Then.framework", 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | shellPath = /bin/sh; 322 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DynamicBottomSheet_Example/Pods-DynamicBottomSheet_Example-frameworks.sh\"\n"; 323 | showEnvVarsInLog = 0; 324 | }; 325 | D8A2CDED63045B3588F59AD5 /* [CP] Check Pods Manifest.lock */ = { 326 | isa = PBXShellScriptBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | ); 330 | inputFileListPaths = ( 331 | ); 332 | inputPaths = ( 333 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 334 | "${PODS_ROOT}/Manifest.lock", 335 | ); 336 | name = "[CP] Check Pods Manifest.lock"; 337 | outputFileListPaths = ( 338 | ); 339 | outputPaths = ( 340 | "$(DERIVED_FILE_DIR)/Pods-DynamicBottomSheet_Example-checkManifestLockResult.txt", 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | shellPath = /bin/sh; 344 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 345 | showEnvVarsInLog = 0; 346 | }; 347 | /* End PBXShellScriptBuildPhase section */ 348 | 349 | /* Begin PBXSourcesBuildPhase section */ 350 | 607FACCC1AFB9204008FA782 /* Sources */ = { 351 | isa = PBXSourcesBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 355 | A40A984B27379A89009CAE9F /* MyViewController.swift in Sources */, 356 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 357 | A40A984F27379AC3009CAE9F /* MyBottomSheetViewController.swift in Sources */, 358 | A40A984D27379AA2009CAE9F /* LabelViewController.swift in Sources */, 359 | A40A985127379E3F009CAE9F /* MyStackViewBottomSheetViewController.swift in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | 607FACE11AFB9204008FA782 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXSourcesBuildPhase section */ 372 | 373 | /* Begin PBXTargetDependency section */ 374 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 375 | isa = PBXTargetDependency; 376 | target = 607FACCF1AFB9204008FA782 /* DynamicBottomSheet_Example */; 377 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 378 | }; 379 | /* End PBXTargetDependency section */ 380 | 381 | /* Begin PBXVariantGroup section */ 382 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 383 | isa = PBXVariantGroup; 384 | children = ( 385 | 607FACDF1AFB9204008FA782 /* Base */, 386 | ); 387 | name = LaunchScreen.xib; 388 | sourceTree = ""; 389 | }; 390 | /* End PBXVariantGroup section */ 391 | 392 | /* Begin XCBuildConfiguration section */ 393 | 607FACED1AFB9204008FA782 /* Debug */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ALWAYS_SEARCH_USER_PATHS = NO; 397 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 398 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 399 | CLANG_CXX_LIBRARY = "libc++"; 400 | CLANG_ENABLE_MODULES = YES; 401 | CLANG_ENABLE_OBJC_ARC = YES; 402 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_COMMA = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 407 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 408 | CLANG_WARN_EMPTY_BODY = YES; 409 | CLANG_WARN_ENUM_CONVERSION = YES; 410 | CLANG_WARN_INFINITE_RECURSION = YES; 411 | CLANG_WARN_INT_CONVERSION = YES; 412 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 414 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 415 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 416 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 417 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 418 | CLANG_WARN_STRICT_PROTOTYPES = YES; 419 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 420 | CLANG_WARN_UNREACHABLE_CODE = YES; 421 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 422 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 423 | COPY_PHASE_STRIP = NO; 424 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | ENABLE_TESTABILITY = YES; 427 | GCC_C_LANGUAGE_STANDARD = gnu99; 428 | GCC_DYNAMIC_NO_PIC = NO; 429 | GCC_NO_COMMON_BLOCKS = YES; 430 | GCC_OPTIMIZATION_LEVEL = 0; 431 | GCC_PREPROCESSOR_DEFINITIONS = ( 432 | "DEBUG=1", 433 | "$(inherited)", 434 | ); 435 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 436 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 437 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 438 | GCC_WARN_UNDECLARED_SELECTOR = YES; 439 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 440 | GCC_WARN_UNUSED_FUNCTION = YES; 441 | GCC_WARN_UNUSED_VARIABLE = YES; 442 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 443 | MTL_ENABLE_DEBUG_INFO = YES; 444 | ONLY_ACTIVE_ARCH = YES; 445 | SDKROOT = iphoneos; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | }; 448 | name = Debug; 449 | }; 450 | 607FACEE1AFB9204008FA782 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_SEARCH_USER_PATHS = NO; 454 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 455 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 456 | CLANG_CXX_LIBRARY = "libc++"; 457 | CLANG_ENABLE_MODULES = YES; 458 | CLANG_ENABLE_OBJC_ARC = YES; 459 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 460 | CLANG_WARN_BOOL_CONVERSION = YES; 461 | CLANG_WARN_COMMA = YES; 462 | CLANG_WARN_CONSTANT_CONVERSION = YES; 463 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INFINITE_RECURSION = YES; 468 | CLANG_WARN_INT_CONVERSION = YES; 469 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 470 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 471 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 472 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 473 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 474 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 475 | CLANG_WARN_STRICT_PROTOTYPES = YES; 476 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 477 | CLANG_WARN_UNREACHABLE_CODE = YES; 478 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 479 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 480 | COPY_PHASE_STRIP = NO; 481 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 482 | ENABLE_NS_ASSERTIONS = NO; 483 | ENABLE_STRICT_OBJC_MSGSEND = YES; 484 | GCC_C_LANGUAGE_STANDARD = gnu99; 485 | GCC_NO_COMMON_BLOCKS = YES; 486 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 487 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 488 | GCC_WARN_UNDECLARED_SELECTOR = YES; 489 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 490 | GCC_WARN_UNUSED_FUNCTION = YES; 491 | GCC_WARN_UNUSED_VARIABLE = YES; 492 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 493 | MTL_ENABLE_DEBUG_INFO = NO; 494 | SDKROOT = iphoneos; 495 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 496 | VALIDATE_PRODUCT = YES; 497 | }; 498 | name = Release; 499 | }; 500 | 607FACF01AFB9204008FA782 /* Debug */ = { 501 | isa = XCBuildConfiguration; 502 | baseConfigurationReference = 37245365F7D8F2010114ADD5 /* Pods-DynamicBottomSheet_Example.debug.xcconfig */; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | INFOPLIST_FILE = DynamicBottomSheet/Info.plist; 506 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 507 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 508 | MODULE_NAME = ExampleApp; 509 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 510 | PRODUCT_NAME = "$(TARGET_NAME)"; 511 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 512 | SWIFT_VERSION = 4.0; 513 | }; 514 | name = Debug; 515 | }; 516 | 607FACF11AFB9204008FA782 /* Release */ = { 517 | isa = XCBuildConfiguration; 518 | baseConfigurationReference = 0581CBD60C17E25C084E9FC6 /* Pods-DynamicBottomSheet_Example.release.xcconfig */; 519 | buildSettings = { 520 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 521 | INFOPLIST_FILE = DynamicBottomSheet/Info.plist; 522 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 523 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 524 | MODULE_NAME = ExampleApp; 525 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 528 | SWIFT_VERSION = 4.0; 529 | }; 530 | name = Release; 531 | }; 532 | 607FACF31AFB9204008FA782 /* Debug */ = { 533 | isa = XCBuildConfiguration; 534 | baseConfigurationReference = FCF403A1528D553F4BF065D4 /* Pods-DynamicBottomSheet_Tests.debug.xcconfig */; 535 | buildSettings = { 536 | FRAMEWORK_SEARCH_PATHS = ( 537 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 538 | "$(inherited)", 539 | ); 540 | GCC_PREPROCESSOR_DEFINITIONS = ( 541 | "DEBUG=1", 542 | "$(inherited)", 543 | ); 544 | INFOPLIST_FILE = Tests/Info.plist; 545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 546 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 549 | SWIFT_VERSION = 4.0; 550 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DynamicBottomSheet_Example.app/DynamicBottomSheet_Example"; 551 | }; 552 | name = Debug; 553 | }; 554 | 607FACF41AFB9204008FA782 /* Release */ = { 555 | isa = XCBuildConfiguration; 556 | baseConfigurationReference = 94D6788AB6CAE9E8D0BFF32D /* Pods-DynamicBottomSheet_Tests.release.xcconfig */; 557 | buildSettings = { 558 | FRAMEWORK_SEARCH_PATHS = ( 559 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 560 | "$(inherited)", 561 | ); 562 | INFOPLIST_FILE = Tests/Info.plist; 563 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 564 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 567 | SWIFT_VERSION = 4.0; 568 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DynamicBottomSheet_Example.app/DynamicBottomSheet_Example"; 569 | }; 570 | name = Release; 571 | }; 572 | /* End XCBuildConfiguration section */ 573 | 574 | /* Begin XCConfigurationList section */ 575 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DynamicBottomSheet" */ = { 576 | isa = XCConfigurationList; 577 | buildConfigurations = ( 578 | 607FACED1AFB9204008FA782 /* Debug */, 579 | 607FACEE1AFB9204008FA782 /* Release */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DynamicBottomSheet_Example" */ = { 585 | isa = XCConfigurationList; 586 | buildConfigurations = ( 587 | 607FACF01AFB9204008FA782 /* Debug */, 588 | 607FACF11AFB9204008FA782 /* Release */, 589 | ); 590 | defaultConfigurationIsVisible = 0; 591 | defaultConfigurationName = Release; 592 | }; 593 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DynamicBottomSheet_Tests" */ = { 594 | isa = XCConfigurationList; 595 | buildConfigurations = ( 596 | 607FACF31AFB9204008FA782 /* Debug */, 597 | 607FACF41AFB9204008FA782 /* Release */, 598 | ); 599 | defaultConfigurationIsVisible = 0; 600 | defaultConfigurationName = Release; 601 | }; 602 | /* End XCConfigurationList section */ 603 | }; 604 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 605 | } 606 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet.xcodeproj/xcshareddata/xcschemes/DynamicBottomSheet-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DynamicBottomSheet 4 | // 5 | // Created by aaronLab on 11/07/2021. 6 | // Copyright (c) 2021 aaronLab. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | 18 | window = UIWindow(frame: UIScreen.main.bounds) 19 | 20 | let viewController = ViewController() 21 | window?.rootViewController = viewController 22 | window?.makeKeyAndVisible() 23 | 24 | return true 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/LabelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelViewController.swift 3 | // DynamicBottomSheet_Example 4 | // 5 | // Created by Aaron Lee on 2021/11/07. 6 | // Copyright © 2021 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | 12 | class LabelViewController: UIViewController { 13 | 14 | // MARK: - Public Properties 15 | 16 | public var label = UILabel() 17 | .then { 18 | $0.numberOfLines = 0 19 | $0.textAlignment = .center 20 | } 21 | 22 | // MARK: - Lifecycle 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | configureView() 27 | } 28 | 29 | // MARK: - Layout 30 | 31 | private func configureView() { 32 | if #available(iOS 13.0, *) { 33 | view.backgroundColor = .tertiarySystemBackground 34 | } else { 35 | view.backgroundColor = .white 36 | } 37 | 38 | layoutLabel() 39 | } 40 | 41 | private func layoutLabel() { 42 | view.addSubview(label) 43 | label.snp.makeConstraints { 44 | $0.top.bottom.equalTo(view.safeAreaLayoutGuide) 45 | $0.leading.trailing.equalToSuperview() 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/MyBottomSheetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyBottomSheetViewController.swift 3 | // DynamicBottomSheet_Example 4 | // 5 | // Created by Aaron Lee on 2021/11/07. 6 | // Copyright © 2021 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import DynamicBottomSheet 11 | 12 | class MyBottomSheetViewController: DynamicBottomSheetViewController { 13 | 14 | // MARK: - Private Properties 15 | 16 | override open var height: CGFloat? { 17 | get { 18 | return view.frame.height / 2 19 | } 20 | set { 21 | super.height = newValue 22 | } 23 | } 24 | 25 | } 26 | 27 | // MARK: - Layout 28 | 29 | extension MyBottomSheetViewController { 30 | 31 | override func configureView() { 32 | super.configureView() 33 | embedContentViewController() 34 | } 35 | 36 | private func embedContentViewController() { 37 | let subViewController = MyViewController() 38 | 39 | let navigationViewController = UINavigationController(rootViewController: subViewController) 40 | navigationViewController.setNavigationBarHidden(false, animated: false) 41 | 42 | addChildViewController(navigationViewController) 43 | 44 | contentView.addSubview(navigationViewController.view) 45 | navigationViewController.view.snp.makeConstraints { 46 | $0.top.leading.trailing.equalToSuperview() 47 | $0.bottom.equalToSuperview() 48 | } 49 | 50 | navigationViewController.didMove(toParentViewController: self) 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/MyStackViewBottomSheetViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyStackViewBottomSheetViewController.swift 3 | // DynamicBottomSheet_Example 4 | // 5 | // Created by Aaron Lee on 2021/11/07. 6 | // Copyright © 2021 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import DynamicBottomSheet 11 | 12 | class MyStackViewBottomSheetViewController: DynamicBottomSheetViewController { 13 | 14 | // MARK: - Private Properties 15 | 16 | private let stackView = UIStackView() 17 | .then { 18 | $0.axis = .vertical 19 | $0.spacing = 32 20 | $0.alignment = .fill 21 | $0.distribution = .fillEqually 22 | } 23 | 24 | } 25 | 26 | // MARK: - Layout 27 | 28 | extension MyStackViewBottomSheetViewController { 29 | 30 | override func configureView() { 31 | super.configureView() 32 | layoutStackView() 33 | } 34 | 35 | private func layoutStackView() { 36 | 37 | contentView.addSubview(stackView) 38 | stackView.snp.makeConstraints { 39 | $0.top.equalToSuperview().offset(16) 40 | $0.leading.equalToSuperview().offset(16) 41 | $0.trailing.equalToSuperview().offset(-16) 42 | $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) 43 | } 44 | 45 | Array(1...5).forEach { 46 | 47 | let label = UILabel() 48 | label.text = "\($0)" 49 | stackView.addArrangedSubview(label) 50 | 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/MyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.swift 3 | // DynamicBottomSheet_Example 4 | // 5 | // Created by Aaron Lee on 2021/11/07. 6 | // Copyright © 2021 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | import RxSwift 12 | import RxCocoa 13 | import Then 14 | 15 | class MyViewController: UIViewController { 16 | 17 | // MARK: - Private Properties 18 | 19 | private var bag = DisposeBag() 20 | 21 | private let stackView = UIStackView() 22 | .then { 23 | $0.axis = .vertical 24 | $0.spacing = 16 25 | $0.alignment = .fill 26 | $0.distribution = .fillEqually 27 | } 28 | 29 | // MARK: - Lifecycle 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | configureView() 35 | } 36 | 37 | } 38 | 39 | // MARK: - Layout 40 | 41 | extension MyViewController { 42 | 43 | private func configureView() { 44 | navigationController?.isNavigationBarHidden = false 45 | title = "My View Controller" 46 | 47 | if #available(iOS 13.0, *) { 48 | view.backgroundColor = .tertiarySystemBackground 49 | } else { 50 | view.backgroundColor = .white 51 | } 52 | 53 | layoutStackView() 54 | layoutButtons() 55 | } 56 | 57 | private func layoutStackView() { 58 | view.addSubview(stackView) 59 | stackView.snp.makeConstraints { 60 | $0.top.equalTo(view.safeAreaLayoutGuide.snp.top) 61 | $0.leading.trailing.equalToSuperview() 62 | $0.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom) 63 | } 64 | } 65 | 66 | private func layoutButtons() { 67 | 68 | let numbers: [Int] = Array(1...3) 69 | numbers.forEach { count in 70 | 71 | let button = UIButton(type: .system) 72 | .then { 73 | $0.setTitle("Button \(count)", for: .normal) 74 | $0.setContentHuggingPriority(.defaultLow, for: .horizontal) 75 | } 76 | 77 | stackView.addArrangedSubview(button) 78 | 79 | button 80 | .rx 81 | .tap 82 | .bind { [weak self] in 83 | DispatchQueue.main.async { 84 | let text: String = "\(count)" 85 | self?.push2LabelViewController(with: text) 86 | } 87 | } 88 | .disposed(by: bag) 89 | 90 | } 91 | 92 | } 93 | 94 | private func push2LabelViewController(with text: String = "N/A") { 95 | let viewController: LabelViewController = LabelViewController() 96 | viewController.label.text = text 97 | viewController.title = text 98 | 99 | if navigationController != nil { 100 | navigationController?.pushViewController(viewController, animated: true) 101 | return 102 | } 103 | 104 | present(viewController, animated: true) 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /Example/DynamicBottomSheet/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DynamicBottomSheet 4 | // 5 | // Created by aaronLab on 11/07/2021. 6 | // Copyright (c) 2021 aaronLab. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | import Then 12 | import RxSwift 13 | import RxCocoa 14 | 15 | class ViewController: UIViewController { 16 | 17 | // MARK: - Private Properties 18 | 19 | private var bag = DisposeBag() 20 | 21 | private let stackView: UIStackView = UIStackView() 22 | .then { 23 | $0.axis = .vertical 24 | $0.spacing = 16 25 | $0.alignment = .fill 26 | $0.distribution = .fill 27 | } 28 | 29 | private let buttonOpenBottomSheet1: UIButton = UIButton(type: .system) 30 | .then { 31 | $0.setTitle("Open Bottom Sheet", for: .normal) 32 | } 33 | 34 | private let buttonOpenBottomSheet2: UIButton = UIButton(type: .system) 35 | .then { 36 | $0.setTitle("Open Bottom Sheet2", for: .normal) 37 | } 38 | 39 | // MARK: - Lifecycle 40 | 41 | override func viewDidLoad() { 42 | super.viewDidLoad() 43 | 44 | configureView() 45 | bindRx() 46 | } 47 | 48 | } 49 | 50 | // MARK: - Layout 51 | 52 | extension ViewController { 53 | 54 | private func configureView() { 55 | 56 | if #available(iOS 13, *) { 57 | view.backgroundColor = .systemBackground 58 | } else { 59 | view.backgroundColor = .white 60 | } 61 | 62 | layoutStackView() 63 | layoutButtonOpenBottomSheet() 64 | } 65 | 66 | private func layoutStackView() { 67 | view.addSubview(stackView) 68 | stackView.snp.makeConstraints { 69 | $0.top.greaterThanOrEqualTo(view.safeAreaLayoutGuide).priority(.low) 70 | $0.bottom.lessThanOrEqualTo(view.safeAreaLayoutGuide).priority(.low) 71 | $0.centerY.equalToSuperview() 72 | $0.leading.trailing.equalToSuperview() 73 | } 74 | } 75 | 76 | private func layoutButtonOpenBottomSheet() { 77 | stackView.addArrangedSubview(buttonOpenBottomSheet1) 78 | stackView.addArrangedSubview(buttonOpenBottomSheet2) 79 | } 80 | 81 | } 82 | 83 | // MARK: - Bind 84 | 85 | extension ViewController { 86 | 87 | private func bindRx() { 88 | bindButtons() 89 | } 90 | 91 | private func bindButtons() { 92 | bindButtonOpenBottomSheet1() 93 | bindButtonOpenBottomSheet2() 94 | } 95 | 96 | private func bindButtonOpenBottomSheet1() { 97 | buttonOpenBottomSheet1 98 | .rx 99 | .tap 100 | .bind { [weak self] in 101 | let bottomSheetViewController = MyBottomSheetViewController() 102 | 103 | DispatchQueue.main.async { 104 | self?.present(bottomSheetViewController, animated: true) 105 | } 106 | } 107 | .disposed(by: bag) 108 | } 109 | 110 | private func bindButtonOpenBottomSheet2() { 111 | buttonOpenBottomSheet2 112 | .rx 113 | .tap 114 | .bind { [weak self] in 115 | let bottomSheetViewController = MyStackViewBottomSheetViewController() 116 | bottomSheetViewController.contentViewBackgroundColor = .orange 117 | 118 | DispatchQueue.main.async { 119 | self?.present(bottomSheetViewController, animated: true) 120 | } 121 | } 122 | .disposed(by: bag) 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | platform :ios, '12.0' 4 | 5 | target 'DynamicBottomSheet_Example' do 6 | pod 'DynamicBottomSheet', :path => '../' 7 | 8 | target 'DynamicBottomSheet_Tests' do 9 | inherit! :search_paths 10 | 11 | 12 | end 13 | end 14 | 15 | post_install do |installer| 16 | installer.pods_project.targets.each do |target| 17 | target.build_configurations.each do |config| 18 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DynamicBottomSheet (0.1.0): 3 | - RxCocoa (~> 6.0) 4 | - RxGesture (~> 4.0) 5 | - RxSwift (~> 6.0) 6 | - SnapKit (~> 5.0) 7 | - Then (~> 2.0) 8 | - RxCocoa (6.2.0): 9 | - RxRelay (= 6.2.0) 10 | - RxSwift (= 6.2.0) 11 | - RxGesture (4.0.2): 12 | - RxCocoa (~> 6.0) 13 | - RxSwift (~> 6.0) 14 | - RxRelay (6.2.0): 15 | - RxSwift (= 6.2.0) 16 | - RxSwift (6.2.0) 17 | - SnapKit (5.0.1) 18 | - Then (2.7.0) 19 | 20 | DEPENDENCIES: 21 | - DynamicBottomSheet (from `../`) 22 | 23 | SPEC REPOS: 24 | trunk: 25 | - RxCocoa 26 | - RxGesture 27 | - RxRelay 28 | - RxSwift 29 | - SnapKit 30 | - Then 31 | 32 | EXTERNAL SOURCES: 33 | DynamicBottomSheet: 34 | :path: "../" 35 | 36 | SPEC CHECKSUMS: 37 | DynamicBottomSheet: ce1b2cad4acd721e8a156901a667b986a1d0760f 38 | RxCocoa: 4baf94bb35f2c0ab31bc0cb9f1900155f646ba42 39 | RxGesture: f059f2aeef966fb17dad56ee440e76410f10e55f 40 | RxRelay: e72dbfd157807478401ef1982e1c61c945c94b2f 41 | RxSwift: d356ab7bee873611322f134c5f9ef379fa183d8f 42 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 43 | Then: acfe0be7e98221c6204e12f8161459606d5d822d 44 | 45 | PODFILE CHECKSUM: bd20b9eb1af61b2011acb854b6ff5e46ce9432c4 46 | 47 | COCOAPODS: 1.11.2 48 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import DynamicBottomSheet 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 aaronLab 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "DynamicBottomSheet", 7 | platforms: [ 8 | .iOS(.v10) 9 | ], 10 | products: [ 11 | .library(name: "DynamicBottomSheet", targets: ["DynamicBottomSheet"]) 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.0.0")), 15 | .package(url: "https://github.com/RxSwiftCommunity/RxGesture.git", .upToNextMajor(from: "4.0.0")), 16 | .package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.0")), 17 | .package(url: "https://github.com/devxoul/Then.git", .upToNextMajor(from: "2.0.0")) 18 | ], 19 | targets: [ 20 | .target( 21 | name: "DynamicBottomSheet", 22 | dependencies: ["RxSwift", "RxCocoa", "RxGesture", "SnapKit", "Then"], 23 | path: "DynamicBottomSheet/Classes" 24 | ) 25 | ], 26 | swiftLanguageVersions: [ 27 | .v5 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamicBottomSheet 2 | 3 | [![Platform](https://img.shields.io/cocoapods/p/DynamicBottomSheet.svg?style=flat)](https://cocoapods.org/pods/DynamicBottomSheet) 4 | [![Language: Swift 5](https://img.shields.io/badge/language-Swift5-orange?style=flat&logo=swift)](https://developer.apple.com/swift) 5 | ![SwiftPM compatible](https://img.shields.io/badge/SPM-compatible-brightgreen?style=flat&logo=swift) 6 | ![CocoaPods compatible](https://img.shields.io/badge/CocoaPods-compatible-brightgreen?style=flat&logo=cocoapods) 7 | [![Version](https://img.shields.io/cocoapods/v/DynamicBottomSheet.svg?style=flat)](https://cocoapods.org/pods/DynamicBottomSheet) 8 | [![License](https://img.shields.io/cocoapods/l/DynamicBottomSheet.svg?style=flat)](https://cocoapods.org/pods/DynamicBottomSheet) 9 | 10 | Fully Customizable Dynamic Bottom Sheet Library for iOS. 11 | 12 | This library doesn't support storyboards. 13 | 14 | However, you can easily override variables in DynamicBottomSheetViewController and make the bottom sheet programmatically. 15 | 16 | ## Preview 17 | 18 | [![Preview](./resources/preview.gif)](./resources/preview.gif) 19 | 20 | ## Requirements 21 | 22 | - Swift 5 23 | 24 | - iOS 10.0 + 25 | 26 | - RxSwift 6.0 27 | 28 | - RxCocoa 6.0 29 | 30 | - RxGesture 4.0 31 | 32 | - SnapKit 5.0 33 | 34 | - Then 2.0 35 | 36 | ## Installation 37 | 38 | ### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) 39 | 40 | DynamicBottomSheet is available through [CocoaPods](https://cocoapods.org). To install 41 | it, simply add the following line to your Podfile: 42 | 43 | ```ruby 44 | pod 'DynamicBottomSheet' 45 | ``` 46 | 47 | ```commandline 48 | $ pod install 49 | ``` 50 | 51 | ### [Swift Package Manager(SPM)](https://www.swift.org/package-manager/) 52 | 53 | In Xcode, add as Swift package with this URL: `https://github.com/aaronLab/DynamicBottomSheet` 54 | 55 | ## Usage 56 | 57 | 1. Import `DynamicBottomSheet` on top of your view controller file. 58 | 59 | 2. Create a class for the bottom sheet where its super class is `DynamicBottomSheetViewController`. 60 | 61 | 3. Put the view you want to show in the `contentView` of the super class `DynamicBottomSheetViewController`. 62 | 63 | 4. Make `constraints of the view you made` to the `contentView` above. 64 | 65 | 5. `Present the bottom sheet view controller` you made before in another view controller. 66 | 67 | ## Example 68 | 69 | For more examples, clone the repo, and run `pod install` from the Example directory. 70 | 71 | ```swift 72 | import UIKit 73 | import DynamicBottomSheet 74 | import SnapKit 75 | import Then 76 | 77 | class MyStackViewBottomSheetViewController: DynamicBottomSheetViewController { 78 | 79 | // MARK: - Private Properties 80 | 81 | private let stackView = UIStackView() 82 | .then { 83 | $0.axis = .vertical 84 | $0.spacing = 32 85 | $0.alignment = .fill 86 | $0.distribution = .fillEqually 87 | } 88 | 89 | } 90 | 91 | // MARK: - Layout 92 | 93 | extension MyStackViewBottomSheetViewController { 94 | 95 | override func configureView() { 96 | super.configureView() 97 | layoutStackView() 98 | } 99 | 100 | private func layoutStackView() { 101 | 102 | contentView.addSubview(stackView) 103 | stackView.snp.makeConstraints { 104 | $0.top.equalToSuperview().offset(16) 105 | $0.leading.equalToSuperview().offset(16) 106 | $0.trailing.equalToSuperview().offset(-16) 107 | $0.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) 108 | } 109 | 110 | Array(1...5).forEach { 111 | 112 | let label = UILabel() 113 | label.text = "\($0)" 114 | stackView.addArrangedSubview(label) 115 | 116 | } 117 | 118 | } 119 | 120 | } 121 | ``` 122 | 123 | ```swift 124 | import UIKit 125 | 126 | class ViewController: UIViewController { 127 | override func viewDidLoad() { 128 | super.viewDidLoad() 129 | 130 | let bottomSheet = MyStackViewBottomSheetViewController() 131 | DispatchQueue.main.async { 132 | self.present(bottomSheet) 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | ## Customization 139 | 140 | ```swift 141 | /// The background color of the view controller below the content view. 142 | /// 143 | /// - `UIColor.black.withAlphaComponent(0.6)` for others. 144 | open var backgroundColor: UIColor 145 | 146 | /// Background view 147 | open var backgroundView: UIView 148 | 149 | /// The background color of the content view. 150 | /// 151 | /// Default value 152 | /// - `UIColor.tertiarySystemBackground` for iOS 13 or later. 153 | /// - `UIColor.white` for others. 154 | open var contentViewBackgroundColor: UIColor 155 | 156 | /// The height of the content view. 157 | /// 158 | /// Default value is `nil` 159 | /// 160 | /// If you set this value explicitly, the height of the content view will be fixed. 161 | open var height: CGFloat? 162 | 163 | /// Content view 164 | open var contentView: UIView 165 | 166 | /// Corner radius of the content view(top left, top right) 167 | /// 168 | /// Default value is `16.0` 169 | open var contentViewCornerRadius: CGFloat 170 | 171 | /// Present / Dismiss transition duration 172 | /// 173 | /// Default value is 0.3 174 | open var transitionDuration: CGFloat 175 | 176 | /// Dismiss velocity threshold 177 | /// 178 | /// Default value is 500 179 | open var dismissVelocityThreshold: CGFloat 180 | ``` 181 | 182 | ## Author 183 | 184 | [Aaron Lee](https://github.com/aaronLab), aaronlab.net@gmail.com 185 | 186 | ## License 187 | 188 | DynamicBottomSheet is available under the MIT license. See the LICENSE file for more info. 189 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /resources/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronlab/DynamicBottomSheet/25595d2314e8c2648feeb0da690748ef1a914889/resources/preview.gif --------------------------------------------------------------------------------