{"id":14006726,"url":"https://github.com/DominikButz/MinimizableView","last_synced_at":"2025-07-24T00:31:57.241Z","repository":{"id":52410992,"uuid":"220432832","full_name":"DominikButz/MinimizableView","owner":"DominikButz","description":"SwiftUI view that minimizes to the bottom of the screen similar to the mini-player in Apple Music or Spotify.","archived":false,"fork":false,"pushed_at":"2024-07-17T08:46:54.000Z","size":52491,"stargazers_count":132,"open_issues_count":0,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-22T17:50:03.595Z","etag":null,"topics":["compact-view","ios","mini-player","swift","swiftui"],"latest_commit_sha":null,"homepage":"","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/DominikButz.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,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-11-08T09:29:23.000Z","updated_at":"2025-07-19T19:14:54.000Z","dependencies_parsed_at":"2024-04-13T18:04:35.123Z","dependency_job_id":null,"html_url":"https://github.com/DominikButz/MinimizableView","commit_stats":{"total_commits":44,"total_committers":2,"mean_commits":22.0,"dds":"0.022727272727272707","last_synced_commit":"1a3f279dd1f5c90f891a358aa80cedfaf88d3aad"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/DominikButz/MinimizableView","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DominikButz%2FMinimizableView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DominikButz%2FMinimizableView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DominikButz%2FMinimizableView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DominikButz%2FMinimizableView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DominikButz","download_url":"https://codeload.github.com/DominikButz/MinimizableView/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DominikButz%2FMinimizableView/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266774745,"owners_count":23982246,"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","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["compact-view","ios","mini-player","swift","swiftui"],"created_at":"2024-08-10T10:01:36.388Z","updated_at":"2025-07-24T00:31:56.442Z","avatar_url":"https://github.com/DominikButz.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"# MinimizableView (iOS 13+ / iPadOS)\n\n[![Version](https://img.shields.io/cocoapods/v/MinimizableView.svg?style=flat)](https://cocoapods.org/pods/MinimizableView)\n[![License](https://img.shields.io/cocoapods/l/MinimizableView.svg?style=flat)](https://cocoapods.org/pods/MinimizableView)\n[![Platform](https://img.shields.io/cocoapods/p/MinimizableView.svg?style=flat)](https://cocoapods.org/pods/MinimizableView)\n\n\n MinimizableView is a simple SwiftUI view for iOS and iPadOS that can minimize like the mini-player in the Spotify or Apple Music app. \n\n**Breaking changes in version 2.0. See details below in the version history**\n\n*Special thanks to Kavsoft ([see here](https://kavsoft.dev/SwiftUI_2.0/Apple_Music/)) - I used parts of their MiniPlayer content in the example. The framework is my own creation though.*\n\n## Example project\n\nThis repo only contains the Swift package, no example code. Please download the example project [here](https://github.com/DominikButz/MinimizableViewExample.git).\nYou need to add the MinimizableView package either through cocoapods or the Swift Package Manager (see below - Installation). \n\n## Features\n\n* Create your own content, background and compact view. The compact view is optional - in case you set it in the initializer, it will appear in the minimized state. \n* By changing the setting properties of the MinimizableViewHandler, you can customize the following properties:\n\t- minimizedHeight\n    - overrideHeight (in case you want to set a height different from the geometry size height)\n\t- lateralMargin\n    - edgesIgnoringSafeAreas\n\nCheck out the examples for details. \n\n\n## Installation\n\n\nInstallation through the Swift Package Manager (SPM) or cocoapods is recommended. \n\nSPM:\nSelect your project (not the target) and then select the Swift Packages tab. Click + and type MinimizableView - SPM should find the package on github. \n\nCocoapods:\n\nplatform :ios, '14.0'\n\ntarget '[project name]' do\n \tpod 'MinimizableView'\nend\n\n\nCheck out the version history below for the current version.\n\n\nMake sure to import MinimizableView in every file where you use the MinimizableView or MinimizableViewHandler\n\n```Swift\nimport MinimizableView\n```\n\n## Usage\n\nCheck out the following example. This repo only contains the Swift package, no example code. Please download the example project [here](https://github.com/DominikButz/MinimizableViewExample.git).\n\n\u003cimg src=\"gitResources/example01.gif\" alt=\"example\" width=\"320\"/\u003e\n\n### Code example: Content View (your main view)\n\nSimply attach the .minimizableView modifier to your main view, e.g. a TabView. \nTo trigger presentation, dismissal, minimization and expansion, you need to call the respective functions of the minimizableViewHandler: present(), dismiss(), minimize() and expand(). It is advisable to call toggleExpansionState() on the minimizableViewHandler whenever you use a tapGesture to toggle the expansion state. \n\n If you don't want a separate compact view, just pass an EmptyView into the compactView closure of the initialiser. The code in the body of MinimizableView checks if compactView is an EmptyView and in that case does not display it.  if there is no compact view, the top of your content will be shown at the bottom of the screen in minimized state.  Use the minimizableViewHandler as EnvironmentObject in your content view - e.g. to remove and insert certain subviews (or to change their opacity) once the minimized property changes (see the example below).\n\nYou also need to attach the minimizableViewHandler as environment object to the MinimizableView. \n\nNEW in version 2.4: update the miniViewBottomMargin parameter dynamically as the height of your tab bar changes. This is achieved with the help of TabBarAccessor. The height can change depending on the device and resizing of the app window (e.g. with stage manager on iPad).\n\n```Swift\n\nstruct RootView: View {\n\n    @ObservedObject var miniHandler: MinimizableViewHandler = MinimizableViewHandler()\n    @State var selectedTabIndex: Int = 0\n    @State var miniViewBottomMargin: CGFloat = 0\n    @GestureState var dragOffset = CGSize.zero\n    @Namespace var namespace\n\n    var body: some View {\n        GeometryReader { proxy in\n\n                TabView(selection: self.$selectedTabIndex) {\n                    \n                    Button(action: {\n                        print(proxy.safeAreaInsets.bottom)\n                        self.miniHandler.present()\n                        \n                    }) { TranslucentTextButtonView(title: \"Launch Minimizable View\", foregroundColor: .green, backgroundColor: .green)}.disabled(self.miniHandler.isPresented)\n                        \n                        .tabItem {\n                            Image(systemName: \"chevron.up.square.fill\")\n                            Text(\"Main View\")\n                    }.tag(0)\n                    .background(TabBarAccessor { tabBar in        // add to update the minimizedBottomMargin dynamically! \n                            self.miniViewBottomMargin = tabBar.bounds.height - 1\n                        })\n                    \n                    Text(\"More stuff\").tabItem {\n                        Image(systemName: \"dot.square.fill\")\n                        Text(\"2nd View\")\n                    }.tag(1)\n                    \n                    ListView(availableWidth: proxy.size.width)\n                        .tabItem {\n                        Image(systemName: \"square.split.2x1.fill\")\n                        Text(\"List View\")\n                    }.tag(2)\n                    \n                    \n                }.background(Color(.secondarySystemFill))\n                .statusBar(hidden: self.miniHandler.isPresented \u0026\u0026 self.miniHandler.isMinimized == false)\n                .minimizableView(content: {ContentExample(animationNamespaceId: self.namespace)},\n                  compactView: {\n                    EmptyView()  // replace EmptyView() by CompactViewExample() to see the a different approach for the compact view\n                }, backgroundView: {\n                    self.backgroundView()},\n                    dragOffset: $dragOffset,\n                    dragUpdating: { (value, state, transaction) in\n                        state = value.translation\n                        self.dragUpdated(value: value)\n   \n                }, dragOnChanged: { (value) in\n                        // add some custom logic if needed\n                },\n                    dragOnEnded: { (value) in\n                    self.dragOnEnded(value: value)\n                }, minimizedBottomMargin: self.miniViewBottomMargin, settings: MiniSettings(minimizedHeight: 80))\n                .environmentObject(self.miniHandler)\n     \n        }\n    \n        //\n    }\n    \n    \n    func backgroundView() -\u003e some View {\n        VStack(spacing: 0){\n            BlurView(style: .systemChromeMaterial)\n            if self.miniHandler.isMinimized {\n                Divider()\n            }\n        }.cornerRadius(self.miniHandler.isMinimized ? 0 : 20)\n        .onTapGesture(perform: {\n            if self.miniHandler.isMinimized {\n                self.miniHandler.expand()\n                //alternatively, override the default animation. self.miniHandler.expand(animation: Animation)\n            }\n        })\n    }\n    \n    \n    func dragUpdated(value: DragGesture.Value) {\n        \n        if self.miniHandler.isMinimized == false \u0026\u0026 value.translation.height \u003e 0   { // expanded state\n            \n            self.miniHandler.draggedOffsetY = value.translation.height  // divide by a factor \u003e 1 for more \"inertia\" if needed\n            \n        } else if self.miniHandler.isMinimized \u0026\u0026 value.translation.height \u003c 0   {// minimized state\n            self.miniHandler.draggedOffsetY = value.translation.height  // divide by a factor \u003e 1 for more \"inertia\" if needed\n            \n        }\n    }\n    \n    func dragOnEnded(value: DragGesture.Value) {\n        \n        if self.miniHandler.isMinimized == false \u0026\u0026 value.translation.height \u003e 90  {\n            self.miniHandler.minimize()\n\n        } else if self.miniHandler.isMinimized \u0026\u0026  value.translation.height \u003c -60 {\n                  self.miniHandler.expand()\n        }\n       withAnimation(.spring()) {\n            self.miniHandler.draggedOffsetY = 0\n       }\n\n    }\n}\n\n\n```\n\n## Change log\n\n#### [Version 2.4.2](https://github.com/DominikButz/MinimizableView/releases/tag/2.4.2)\nUpdated default value of mini settings minimumDragDistance to 1 in order to prevent unresponsive subviews in the content view, e.g. List scrolling and dragging a slider. \n\n#### [Version 2.4.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.4.1)\nupdating readme after merge with pull request crossplatform project friendliness #9. Excluding macOS conflicts through pre-processor conditionals limiting package usage to iOS / iPadOS.\n\n#### [Version 2.4](https://github.com/DominikButz/MinimizableView/releases/tag/2.4)\n\n* initializer update: removed geometry parameter\n* presentation is now done properly with a move-transition\n* simplified calculation of position and offset of mini view\n* added TabBarAccessor UIViewRepresentable struct. see the example project and updated readme on how to use this. The bottom line is that it helps to update minimizedBottomMargin dynamically in reaction to a change of the tab bar view height. \n\n#### [Version 2.3.3](https://github.com/DominikButz/MinimizableView/releases/tag/2.3.3)\nFixes presentation and dismiss transition bug that would move the background out of the view separately from the content.\n\n#### [Version 2.3.2](https://github.com/DominikButz/MinimizableView/releases/tag/2.3.2)\nAdded minimumDragDistance to settings. If your content view contains a List, make sure to set this value \u003e 0 (usually between 10 and 30 is a suitable value) - this will make sure the List is scrollable.\n\n#### [Version 2.3.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.3.1)\nBug fix: If the user drags the mini view up or down and simultaneously does a pan gesture (magnifiying gesture), mini view does not freeze any more but minimizes instead. \n\n#### [Version 2.3](https://github.com/DominikButz/MinimizableView/releases/tag/2.3)\nBreaking change: added dragOffset parameter and dragUpdating closure. It is possible to move the dragOnChange logic to the dragUpdating closure (see updated example code).\n\n#### [Version 2.2.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.2.1)\nContent view and compact view are now clipped to prevent subviews in the content view from still being visible despite being minimized. This removes the necessity to apply the clipped modifier on your custom content and compact views. Since the subviews of your content view disappear properly now once the mini view compresses, it is not necessary any more to conditionally show and remove subviews of your content view according to minimized and expanded states (exceptions:  subviews with matched geometry effect and removing / showing view elements on the top bar of your content view if you don't use a separate compact view).\n\n#### [Version 2.2](https://github.com/DominikButz/MinimizableView/releases/tag/2.2)\niOS 15 update: Removed animation from settings because the animation modifier on the miniView can impact the content view's subviews' animations and lead to weird behavior. Instead the miniView handler functions present(), expand(),  minimize() and toggleExpansionState() have now (an) animation parameter(s) that can be overridden (default is .spring()).\n\n#### [Version 2.1.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.1.1)\niOS 15 update: fixed a bug that could trigger an infinite loop while dragging the mini view upwards (in minimized state).\n\n#### [Version 2.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.1)\nAdded edgesIgnoringSafeArea to settings. Default value is [.bottom, .top]. Make sure to add top padding to your content if required. \n\n#### [Version 2.0.2](https://github.com/DominikButz/MinimizableView/releases/tag/2.0.2)\nBug fix: When in minimized state and the keyboard appears, the miniView will now correctly disappear and reappear after the keyboard is dismissed.\n\n#### [Version 2.0.1](https://github.com/DominikButz/MinimizableView/releases/tag/2.0.1)\nMoved minimizedBottomMargin to the miniView initializer. This is useful e.g. in case of a changing distance to the bottom edge according to the screen orientation. \n\n#### [Version 2.0](https://github.com/DominikButz/MinimizableView/releases/tag/2.0)\nBreaking Changes. the following parameters need to be set in the initialiser: \n- backgroundView\n- onDragChanged and onDragEnded  \n- settings (optional)\n\n#### [Version 1.2.1](https://github.com/DominikButz/MinimizableView/releases/tag/1.2.1)\nBug fix: when in minimized state, the mini view will disappear if the keyboard shows (instead of floating above the keyboard).\n\n#### [Version 1.2](https://github.com/DominikButz/MinimizableView/releases/tag/1.2)\nThe compactView parameter cannot be nil. If you don't want a separate compactView, pass in an EmptyView. \nRemoved transitions from minimizableView body (contentView and compactView). Instead, attach the transition modifier to your implementation of conentView and compactView. Check out the example repository for details.\nParameters of the MiniSettings struct can now be set directly in the initializer.\n\n#### [Version 1.1.1](https://github.com/DominikButz/MinimizableView/releases/tag/1.1.1)\nSlight animation improvement.\n\n#### [Version 1.1](https://github.com/DominikButz/MinimizableView/releases/tag/1.1)\nContent view now only appears if the mini view is presented. Other minor improvements.\n\n#### [Version 1.0](https://github.com/DominikButz/MinimizableView/releases/tag/1.0)\nBreaking change of initializer: Content view and compact view now need to be inserted into closures, no more casting to AnyView! Bug fix: top of mini view does not show any more when in hidden state in case the UI device is without home button (e.g. iPhone 11 max). Bonus: convenience modifier (see example).\n\n#### [Version 0.3.2](https://github.com/DominikButz/MinimizableView/releases/tag/0.3.2)\nBug fixes: onMinimization is now called as expected. onExpansion is only called when isPresented is true. \n\n#### [Version 0.3.1](https://github.com/DominikButz/MinimizableView/releases/tag/0.3.1)\nAdding safety margin to offsetY when minimizable view presentation state is false - this fixes the shadow visibility bug at the bottom of the screen.\n\n#### [Version 0.3](https://github.com/DominikButz/MinimizableView/releases/tag/0.3)\nExpansion / minimization through the VerticalDragGesture modifier is now triggered only after the drag gesture ended. The VerticalDragGesture view modifier is now internal to the framework - instead *use the modifier function verticalDragGesture(translationHeightTriggerValue: CGFloat)*. Bug fixes. \n\n#### [Version 0.2.1](https://github.com/DominikButz/MinimizableView/releases/tag/0.2.1)\nUpdated frame height and offsetY functions to allow expanding the minimized frame when dragging upwards.\n\n#### [Version 0.2](https://github.com/DominikButz/MinimizableView/releases/tag/0.2)\nInitial public release. \n\n\n## Author\n\ndominikbutz@gmail.com\n\n## License\n\nMinimizableView is available under the MIT license. See the LICENSE file for more info.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDominikButz%2FMinimizableView","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDominikButz%2FMinimizableView","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDominikButz%2FMinimizableView/lists"}