├── .gitignore ├── .swiftpm └── xcode │ ├── package.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ └── xcschemes │ ├── TranslucentWindowStyle.xcscheme │ └── TranslucentWindowStyleTest.xcscheme ├── Package.swift ├── README.md ├── Sources └── TranslucentWindowStyle │ └── TranslucentWindowStyle.swift ├── Tests └── TranslucentWindowStyleTests │ └── TranslucentWindowStyleTests.swift └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/TranslucentWindowStyle.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 61 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/TranslucentWindowStyleTest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 68 | 69 | 75 | 76 | 82 | 83 | 84 | 85 | 87 | 88 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "TranslucentWindowStyle", 8 | platforms: [.macOS(.v12)], 9 | products: [ 10 | // Products define the executables and libraries a package produces, and make them visible to other packages. 11 | .library( 12 | name: "TranslucentWindowStyle", 13 | targets: ["TranslucentWindowStyle"]), 14 | ], 15 | dependencies: [ 16 | // Dependencies declare other packages that this package depends on. 17 | // .package(url: /* package url */, from: "1.0.0"), 18 | ], 19 | targets: [ 20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 21 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 22 | .target( 23 | name: "TranslucentWindowStyle", 24 | dependencies: []), 25 | .testTarget( 26 | name: "TranslucentWindowStyleTests", 27 | dependencies: ["TranslucentWindowStyle"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TranslucentWindowStyle 2 | 3 | Translucent Window Background Style is a Swift package with a custom SwiftUI window background style that creates a translucent window with a blur effect. This package allows you to add a translucent background style to your SwiftUI app's windows. 4 | 5 | ## Overview 6 | 7 | The Translucent Window Background Style package provides a `TranslucentBackgroundStyle` struct that conforms to the `WindowBackgroundStyle` protocol. This style creates a translucent window with a blur effect. 8 | 9 | ## Usage 10 | 11 | To use the Translucent Window Background Style in your SwiftUI app, follow these steps: 12 | 13 | 1. Import the package in your SwiftUI view file: 14 | 15 | ```swift 16 | import TranslucentWindowBackgroundStyle 17 | ``` 18 | 19 | 2. Apply the `presentedWindowBackgroundStyle` modifier to your window view, and pass in an instance of `TranslucentBackgroundStyle`: 20 | 21 | ```swift 22 | WindowGroup { 23 | ContentView() 24 | .presentedWindowBackgroundStyle(.hiddenTitleBarTranslucent) 25 | } 26 | ``` 27 | 28 | 3. The `hiddenTitleBarTranslucent` static property of `TranslucentBackgroundStyle` provides a convenient method to create a translucent window without a title bar. 29 | 30 | ```swift 31 | TranslucentBackgroundStyle.hiddenTitleBarTranslucent 32 | ``` 33 | 34 | ### Example 35 | 36 | ![screenshot](screenshot.png) 37 | 38 | ## Installation 39 | 40 | The Translucent Window Background Style package can be installed via Swift Package Manager. To install, follow these steps: 41 | 42 | 1. Open your project in Xcode. 43 | 44 | 2. Click on File > Swift Packages > Add Package Dependency. 45 | 46 | 3. Enter the following URL in the search bar: 47 | 48 | ```javascript 49 | https://github.com/DominatorVbN/TranslucentWindowBackgroundStyle 50 | ``` 51 | 52 | Replace "your-username" with your GitHub username or the URL of your forked repository if you want to used the foked version in your app. 53 | 54 | 4. Choose the version or branch of the package that you want to install. 55 | 56 | 5. Click on the Add Package button. 57 | 58 | 6. Add `TranslucentWindowBackgroundStyle` to the list of dependencies for your target in your project's `Package.swift` file: 59 | 60 | ```swift 61 | dependencies: [ 62 | .package(url: "https://github.com/DominatorVbN/TranslucentWindowBackgroundStyle", .upToNextMinor(from: "1.0.0")) 63 | ] 64 | ``` 65 | 66 | Replace "your-username" with your GitHub username or the URL of your forked repository if you want to used the foked version in your app., and "1.0.0" with the version or branch that you installed. 67 | 68 | 7. Import the package in your SwiftUI view file: 69 | 70 | ```swift 71 | import TranslucentWindowBackgroundStyle 72 | ``` 73 | 74 | 8. You're now ready to use the Translucent Window Background Style in your SwiftUI app! 75 | 76 | ## Contributing 77 | 78 | Contributions are always welcome, whether it's bug fixes, feature enhancements, or documentation improvements. To contribute, please follow these steps: 79 | 80 | 1. Fork the repository 81 | 2. Create a new branch for your changes: `git checkout -b feature/your-feature-name` 82 | 3. Make your changes and commit them: `git commit -m 'Add some feature'` 83 | 4. Push your changes to your forked repository: `git push origin feature/your-feature-name` 84 | 5. Create a pull request on the original repository, with a description of your changes 85 | 86 | Thank you for your contributions! 87 | -------------------------------------------------------------------------------- /Sources/TranslucentWindowStyle/TranslucentWindowStyle.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | 4 | public protocol WindowBackgroundStyle { 5 | associatedtype Body : View 6 | @ViewBuilder @MainActor var backgroud: Self.Body { get } 7 | } 8 | 9 | extension WindowBackgroundStyle where Self == TranslucentBackgroundStyle { 10 | public static var hiddenTitleBar: TranslucentBackgroundStyle { TranslucentBackgroundStyle(config: .translucent) } 11 | 12 | public static func hiddenTitleBar(material: NSVisualEffectView.Material) -> TranslucentBackgroundStyle { 13 | TranslucentBackgroundStyle(config: .translucent(material: material)) 14 | } 15 | } 16 | 17 | public struct TranslucentBackgroundStyle: WindowBackgroundStyle { 18 | let config: TranslucentWindowBackground.WindowConfiguration 19 | public var backgroud: some View { 20 | TranslucentWindowBackground(config: config) 21 | } 22 | } 23 | 24 | extension View { 25 | 26 | @MainActor public func presentedWindowBackgroundStyle(_ style: S) -> some View where S : WindowBackgroundStyle { 27 | self.background(style.backgroud) 28 | } 29 | 30 | } 31 | 32 | struct TranslucentWindowBackground: NSViewRepresentable { 33 | 34 | let config: WindowConfiguration 35 | 36 | enum ContentViewConfiguration { 37 | case embed(NSView?) 38 | case replace(NSView?) 39 | } 40 | 41 | enum StyleMaskConfiguration { 42 | case insert(NSWindow.StyleMask) 43 | case replace(NSWindow.StyleMask) 44 | } 45 | 46 | struct WindowConfiguration { 47 | let isOpaque: Bool 48 | let backgroundColor: NSColor 49 | let contentViewCofiguration: ContentViewConfiguration 50 | let styleMaskConfiguration: StyleMaskConfiguration 51 | let titlebarAppearsTransparent: Bool 52 | let titleVisibility: NSWindow.TitleVisibility 53 | let standardWindowButtonConfig: StandardWindowButtonConfiguration 54 | let isMovableByWindowBackground: Bool 55 | 56 | public struct StandardWindowButtonConfiguration { 57 | let miniaturizeButtonIsHidden: Bool 58 | let closeButtonIsHidden: Bool 59 | let zoomButtonIsHidden: Bool 60 | } 61 | 62 | static func getTranlucentBackground(material: NSVisualEffectView.Material) -> NSView { 63 | let visualEffect = NSVisualEffectView() 64 | visualEffect.blendingMode = .behindWindow 65 | visualEffect.state = .followsWindowActiveState 66 | visualEffect.material = material 67 | return visualEffect 68 | } 69 | 70 | static let translucent: WindowConfiguration = WindowConfiguration( 71 | isOpaque: false, 72 | backgroundColor: NSColor.clear, 73 | contentViewCofiguration: .embed(getTranlucentBackground(material: .sidebar)), 74 | styleMaskConfiguration: .insert(.titled), 75 | titlebarAppearsTransparent: true, 76 | titleVisibility: .hidden, 77 | standardWindowButtonConfig: StandardWindowButtonConfiguration( 78 | miniaturizeButtonIsHidden: true, 79 | closeButtonIsHidden: true, 80 | zoomButtonIsHidden: true 81 | ), 82 | isMovableByWindowBackground: true 83 | ) 84 | 85 | static func translucent(material: NSVisualEffectView.Material) -> WindowConfiguration { 86 | WindowConfiguration( 87 | isOpaque: false, 88 | backgroundColor: NSColor.clear, 89 | contentViewCofiguration: .embed(getTranlucentBackground(material: material)), 90 | styleMaskConfiguration: .insert(.titled), 91 | titlebarAppearsTransparent: true, 92 | titleVisibility: .hidden, 93 | standardWindowButtonConfig: StandardWindowButtonConfiguration( 94 | miniaturizeButtonIsHidden: true, 95 | closeButtonIsHidden: true, 96 | zoomButtonIsHidden: true 97 | ), 98 | isMovableByWindowBackground: true 99 | ) 100 | } 101 | 102 | static func configure(window: NSWindow, forConfig config: WindowConfiguration) { 103 | 104 | window.isOpaque = config.isOpaque 105 | window.backgroundColor = config.backgroundColor 106 | 107 | switch config.contentViewCofiguration { 108 | case let .replace(view): 109 | window.contentView = view 110 | case let .embed(view): 111 | let currentContentView = window.contentView 112 | window.contentView = view 113 | if let currentContentView = currentContentView { 114 | view?.addSubview(currentContentView) 115 | } 116 | } 117 | 118 | switch config.styleMaskConfiguration { 119 | case let .replace(mask): 120 | window.styleMask = mask 121 | case let .insert(mask): 122 | window.styleMask.insert(mask) 123 | } 124 | 125 | window.titlebarAppearsTransparent = config.titlebarAppearsTransparent 126 | window.titleVisibility = config.titleVisibility 127 | window.standardWindowButton(.miniaturizeButton)?.isHidden = config.standardWindowButtonConfig.miniaturizeButtonIsHidden 128 | window.standardWindowButton(.closeButton)?.isHidden = config.standardWindowButtonConfig.closeButtonIsHidden 129 | window.standardWindowButton(.zoomButton)?.isHidden = config.standardWindowButtonConfig.zoomButtonIsHidden 130 | window.isMovableByWindowBackground = config.isMovableByWindowBackground 131 | } 132 | } 133 | 134 | 135 | func makeCoordinator() -> Coordinator { 136 | Coordinator(config: config) 137 | } 138 | 139 | 140 | class Coordinator: NSObject { 141 | 142 | let config: WindowConfiguration 143 | private var _originalWindowConfiguration: WindowConfiguration? 144 | 145 | init(config: WindowConfiguration) { 146 | self.config = config 147 | } 148 | 149 | func createWindowConfigurationForCurrentContext(_ context: NSWindow) -> WindowConfiguration { 150 | WindowConfiguration( 151 | isOpaque: context.isOpaque, 152 | backgroundColor: context.backgroundColor, 153 | contentViewCofiguration: .replace(context.contentView), 154 | styleMaskConfiguration: .replace(context.styleMask), 155 | titlebarAppearsTransparent: context.titlebarAppearsTransparent, 156 | titleVisibility: context.titleVisibility, 157 | standardWindowButtonConfig: WindowConfiguration.StandardWindowButtonConfiguration( 158 | miniaturizeButtonIsHidden: context.standardWindowButton(.miniaturizeButton)?.isHidden ?? false, 159 | closeButtonIsHidden: context.standardWindowButton(.closeButton)?.isHidden ?? false, 160 | zoomButtonIsHidden: context.standardWindowButton(.zoomButton)?.isHidden ?? false 161 | ), 162 | isMovableByWindowBackground: context.isMovableByWindowBackground 163 | ) 164 | } 165 | 166 | func makeWindowTranslucent(window: NSWindow?) { 167 | guard let window = window else { return } 168 | self._originalWindowConfiguration = createWindowConfigurationForCurrentContext(window) 169 | let translucentWindowConfiguration = config 170 | WindowConfiguration.configure(window: window, forConfig: translucentWindowConfiguration) 171 | } 172 | 173 | func resetWindow(window: NSWindow) { 174 | if let originalWindowConfiguration = _originalWindowConfiguration { 175 | WindowConfiguration.configure(window: window, forConfig: originalWindowConfiguration) 176 | } 177 | } 178 | } 179 | 180 | func makeNSView(context: Context) -> NSView { 181 | let view = NSView() 182 | DispatchQueue.main.async { 183 | let window = view.window 184 | context.coordinator.makeWindowTranslucent(window: window) 185 | } 186 | return view 187 | } 188 | 189 | func updateNSView(_ nsView: NSView, context: Context) { 190 | 191 | } 192 | 193 | static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) { 194 | guard let window = nsView.window else { 195 | return 196 | } 197 | coordinator.resetWindow(window: window) 198 | } 199 | } 200 | 201 | 202 | -------------------------------------------------------------------------------- /Tests/TranslucentWindowStyleTests/TranslucentWindowStyleTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import TranslucentWindowStyle 3 | 4 | final class TranslucentWindowStyleTests: XCTestCase { 5 | } 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominatorVbN/TranslucentWindowStyle/67833e921348b69e1c3c560734a374bdf5bc0504/screenshot.png --------------------------------------------------------------------------------