{"id":13616540,"url":"https://github.com/radianttap/CardPresentationController","last_synced_at":"2025-04-14T00:32:31.994Z","repository":{"id":50251364,"uuid":"161066477","full_name":"radianttap/CardPresentationController","owner":"radianttap","description":"Custom UIPresentationController which mimics the behavior of Apple Music UI","archived":true,"fork":false,"pushed_at":"2021-06-15T19:57:46.000Z","size":12070,"stargazers_count":794,"open_issues_count":3,"forks_count":41,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-10-13T11:34:35.602Z","etag":null,"topics":["apple-music","cards","custom","popup","transitions","uikit","uipresentationcontroller"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/radianttap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-09T17:59:00.000Z","updated_at":"2024-07-03T02:36:25.000Z","dependencies_parsed_at":"2022-08-12T21:00:53.469Z","dependency_job_id":null,"html_url":"https://github.com/radianttap/CardPresentationController","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radianttap%2FCardPresentationController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radianttap%2FCardPresentationController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radianttap%2FCardPresentationController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radianttap%2FCardPresentationController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/radianttap","download_url":"https://codeload.github.com/radianttap/CardPresentationController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223612181,"owners_count":17173590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-music","cards","custom","popup","transitions","uikit","uipresentationcontroller"],"created_at":"2024-08-01T20:01:29.952Z","updated_at":"2024-11-08T00:32:06.787Z","avatar_url":"https://github.com/radianttap.png","language":"Swift","funding_links":["https://www.buymeacoffee.com/radianttap"],"categories":["HarmonyOS","Swift"],"sub_categories":["Windows Manager"],"readme":"This component is deprecated.\n\n- We have native card support since iOS 13. \n- In iOS 15 Apple is adding custom presentation sheets for half-modals.\n\nThis component has served its purpose, **please move on to native API**.\n\n---\n\n\n[![](https://img.shields.io/github/tag/radianttap/CardPresentationController.svg?label=current)](https://github.com/radianttap/CardPresentationController/releases)\n![platforms: iOS](https://img.shields.io/badge/platform-iOS-blue.svg)\n[![](https://img.shields.io/github/license/radianttap/CardPresentationController.svg)](https://github.com/radianttap/CardPresentationController/blob/master/LICENSE)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-AD4709.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![CocoaPods compatible](https://img.shields.io/badge/CocoaPods-compatible-fb0006.svg)](https://cocoapods.org)\n![](https://img.shields.io/badge/swift-5-223344.svg?logo=swift\u0026labelColor=FA7343\u0026logoColor=white)\n\n# CardPresentationController\n\nCustom [UIPresentationController](https://developer.apple.com/documentation/uikit/uipresentationcontroller) which mimics the behavior of Apple Music UI. Should work just fine from iOS 10 and beyond.\n\n[DEMO video on iPhone Xs simulator](CardPresentationController.mp4)\n\n### Modal presentation in iOS 13\n\niOS 13 changed the behavior of the ordinary `present(vc, ...) calls` - all modals now look like cards. *Thus you don’t need this library on iOS 13.* I always recommend to use system stuff as much as possible thus this library will, by default, fallback to system look \u0026 behavior if you are on iOS 13. \n\nTo toggle this off and still use this library to present modal cards, set this at some point before presenting your first `UIViewController`:\n\n```\nCardPresentationController.useSystemPresentationOniOS13 = false\n```\n\nKeep in mind that visual display of multiple cards in this library is different from what iOS 13 does. (I don’t intend to change this, it’s not really worth it.)\n\n## Installation\n\n### Manually\n\nAdd the folder `CardPresentationController` into your project. It's only five files.\n\nIf you prefer to use dependency managers, see below. \nReleases are tagged with [Semantic Versioning](https://semver.org) in mind.\n\n### CocoaPods\n\n[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Coordinator into your Xcode project using CocoaPods, specify it in your `Podfile`:\n\n```ruby\npod 'CardPresentationController', \t:git =\u003e 'https://github.com/radianttap/CardPresentationController.git'\n```\n\n### Setting up with Carthage\n\n[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.\n\nYou can install Carthage with [Homebrew](http://brew.sh/) using the following command:\n\n```bash\n$ brew update\n$ brew install carthage\n```\n\nTo integrate CardPresentationController into your Xcode project using Carthage, specify it in your `Cartfile`:\n\n```ogdl\ngithub \"radianttap/CardPresentationController\"\n```\n\n\n\n## Usage\n\nFrom anywhere you want to present some `UIViewController`, call\n\n```swift\nlet vc = ...\npresentCard(vc, animated: true)\n```\n\nYou dismiss it as any other modal:\n\n```swift\ndismiss(animated: true)\n```\n\nThis will present `vc` modally, flying-in from the bottom edge. Existing view will be kept shown as dimmed background card, on black background.\n\nYou can *present card from another card*; library will stack the cards nicely. Do use common sense as popups over popups don’t make pleasant user experience.\n\n### Advanced behavior\n\nView of the `presenting` Controller will be (by default) 20% transparent to blend into the background a bit, thus looking dimmed.\n\nThat back \"card\" is also inset a bit from the edges.\n\n![](resources/presentedNC-top.png)\n\nIf the _presented_ VC is `UINavigationController` instance, nothing special happens. It’s assumed that you will add `UIBarButtonItem` which will facilitate dismissal.\n\nIf it is not, then `CardPresentationController` will automatically add a button at the middle of the shown card. Tapping on that will dismiss the card.\n\n![](resources/presentedVC-top.png)\n\nAs you present card over card, back cards will be ever more transparent and horizontally inset. In most cases, this should look rather nice.\n\nLibrary also supports interactive dismissal — simply pan  from top to bottom and UI will obey you. You can pan up or down and the direction and position where you let go will determine will the card finish dismissing or return to presented state.\n\n### Status bar style\n\nCardPresentationController tries its best to enforce `.lightContent` status bar style. You can help it, by adding this into your UIVC subclass:\n\n```swift\noverride var preferredStatusBarStyle: UIStatusBarStyle {\n\treturn .lightContent\n}\n```\n\nIf you are presenting UINC, then my advice is to subclass it and override `preferredStatusBarStyle` property in the same way.\n\n## Requirements\n\n*Requires iOS 10*, since it uses [UIViewPropertyAnimator](https://developer.apple.com/documentation/uikit/uiviewpropertyanimator), [UISpringTimingParameters](https://developer.apple.com/documentation/uikit/uispringtimingparameters) and a bunch of other modern UIKit animation APIs.\n\nOn iOS 11 it uses [maskedCorners](https://developer.apple.com/documentation/quartzcore/calayer/2877488-maskedcorners) property to round just the top corners. On iOS 10.x it will fallback to rounding all corners.\n\n## How it works\n\nThe main object here is `CardTransitionManager`, which acts as  `UIViewControllerTransitioningDelegate`. It is internally instantiated and assigned as property on UIVC which called `presentCard()` – that's _sourceController_ in the UIPresentationController parlance.\n\nThis instance of CTM is automatically removed on dismissal.\n\nCTM creates and manages the other two required objects:\n\n* `CardPresentationController`: manages additional views (like dismiss handle at the top of the card) and other aspects of the custom presentation\n* `CardAnimator`: which performs the animated transition\n\nIn case you missed it — *you don’t deal with any of that*. It’s all implementation detail, hidden inside these 3 classes. You never instantiate them directly.\n\nThe only object you can put to use, if you want to, is…\n\n### CardConfiguration\n\nWhen calling `presentCard`, you can supply optional `CardConfiguration` instance. This is simple struct containing the following parameters:\n\n```swift\n///\tVertical inset from the top or already shown card\nvar verticalSpacing: CGFloat = 16\n\n///\tLeading and trailing inset for the existing (presenting) view \n/// when it's being pushed further back\nvar horizontalInset: CGFloat = 16\n\n///\tHeight of the \"empty\" area at the top of the card \n///\twhere dismiss handle glyph will be centered.\npublic var dismissAreaHeight: CGFloat = 16\n\n///\tCards have rounded corners, right?\nvar cornerRadius: CGFloat = 12\n\n///\tThe starting frame for the presented card.\nvar initialTransitionFrame: CGRect?\n\n///\tHow much to fade the back card.\nvar backFadeAlpha: CGFloat = 0.8\n\n///\tSet to false to disable interactive dismissal\nvar allowInteractiveDismissal = true\n```\n\nThere’s a very handy `init` for it where you can supply any combination of these parameters.\n\nIf you don't supply config, then `CardConfiguration.shared` will be used, consisting of the default values shown above. \nYou can override this property early in app's lifecycle so adjust default look of the cards for the entire app (see AppDelegate.swift for an example).  \n\n### Advanced example\n\nThus if you want to control where the card originates — say if you want to mimic Apple Music's now-playing card — you can:\n\n```swift\nlet vc = ContentController.instantiate()\n\nlet f = container.convert(sender.bounds, to: view.window!)\nlet config = CardConfiguration(initialTransitionFrame: f)\n\npresentCard(vc, configuration: config, animated: true)\n```\n\nThe important bit here is setting `initialTransitionFrame` property to the frame *in the UIWindow coordinating space*, since transition happens in it.\n\n### Caveats\n\n`CardAnimator` animates layout of its own subviews – `from` and `to` views included in `transitionContext`. Behavior and layout of the internal subviews of both _presented_ and _presenting_/_source_ views is up to you *but* CardAnimator will try its best to animate them along.\n\nDepending on the complexity of your UI, in may be impossible to make the transition perfect. Usually in cases where UIKit applies its own private API magic related to status / navigation bars. \nSee `EmbeddedNCExample` where I have `UINavigationController` embedded inside ordinary `UIViewController`. This is very unusual UIVC stack which I would love to solve since I have project using just that.\n\n## LICENSE\n\n[MIT](LICENSE), as usual for all my stuff.\n\n\n## Give back\n\nIf you found this code useful, please consider [buying me a coffee](https://www.buymeacoffee.com/radianttap) or two. ☕️😋\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradianttap%2FCardPresentationController","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fradianttap%2FCardPresentationController","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradianttap%2FCardPresentationController/lists"}