How to fit ScrollView to content in SwiftUI

Issue #769 If we place ScrollView inside HStack or VStack, it takes all remaining space. To fit ScrollView to its content, we need to get its content size and constrain ScrollView size. Use a GeometryReader as Scrollview content background, and get the local frame import SwiftUI struct HSearchBar: View { @State private var scrollViewContentSize: CGSize = .zero var body: some View { HStack { searchButton ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 12) { ForEach(store....

February 6, 2021 · 1 min · 104 words · Khoa

How to show modal window in SwiftUI for macOS

Issue #768 Use custom NSWindow, set level in becomeKey and call NSApp.runModal to show modal final class ModalWindow: NSWindow { override func becomeKey() { super.becomeKey() level = .statusBar } override func close() { super.close() NSApp.stopModal() } } let window = ModalWindow( contentRect: .zero, styleMask: [.titled, .closable], backing: .buffered, defer: false ) window.titlebarAppearsTransparent = true window.title = "Manage collections" window.center() window.isReleasedWhenClosed = false self.window = window let view = CollectionSettingsView(store: Store.shared) ....

February 3, 2021 · 1 min · 106 words · Khoa

How to use custom Key for NSCache

Issue #766 Need to use a class, best is to subclass from NSObject let cache = NSCache<Key, UIImage>() final class Key: NSObject { override func isEqual(_ object: Any?) -> Bool { guard let other = object as? Key else { return false } return url == other.url && size == other.size } override var hash: Int { return url.hashValue ^ Int(size.width) ^ Int(size.height) } let url: URL let size: CGSize init(url: URL, size: CGSize) { self....

January 30, 2021 · 1 min · 83 words · Khoa

How to show multiple popover in SwiftUI

Issue #765 In SwiftUI currently, it’s not possible to attach multiple .popover to the same View. But we can use condition to show different content struct ClipboardCell: View { enum PopoverStyle { case raw case preview } @Binding var showsPopover: Bool @Binding var popoverStyle: PopoverStyle var body: some View { VStack { header content .popover(isPresented: $showsPopover) { switch popoverStyle { case .raw: ViewRawView(item: item) case .preview: PreviewView(item: item) } } footer } } }

January 29, 2021 · 1 min · 74 words · Khoa

How to handle keyDown in SwiftUI for macOS

Issue #764 Use a custom KeyAwareView that uses an NSView that checks for keyDown method. In case we can’t handle certain keys, call super.keyDown(with: event) import SwiftUI import KeyboardShortcuts struct KeyAwareView: NSViewRepresentable { let onEvent: (Event) -> Void func makeNSView(context: Context) -> NSView { let view = KeyView() view.onEvent = onEvent DispatchQueue.main.async { view.window?.makeFirstResponder(view) } return view } func updateNSView(_ nsView: NSView, context: Context) {} } extension KeyAwareView { enum Event { case upArrow case downArrow case leftArrow case rightArrow case space case delete case cmdC } } private class KeyView: NSView { var onEvent: (KeyAwareView....

January 29, 2021 · 1 min · 161 words · Khoa

How to extend custom View in SwiftUI

Issue #763 I usually break down a big struct into smaller views and extensions. For example I have a ClipboardCell that has a lot of onReceive so I want to move these to another component. One way to do that is to extend ClipboardCell struct ClipboardCell: View { let isSelected: Bool @State var showsPreview: Bool @State var showsViewRaw: Bool let onCopy: () -> Void let onDelete: () -> Void } extension ClipboardCell { func onReceiveKeyboard() -> some View { self....

January 28, 2021 · 3 min · 431 words · Khoa

How to use Sparkle for macOS app

Issue #762 Install Sparkle For now, the latest stable version is 1.24.0 which supports CocoaPods OK, but still, have issues with SPM. Support non sandboxed apps Version 2.0.0 is in beta and supports sandboxed apps To install, use CocoaPods platform :osx, '11.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'Sparkle' end Sign In your target, choose Signing & Capability tab, change Signing Certificate from Locally to Development for code sign to work for embedded frameworks...

January 26, 2021 · 2 min · 295 words · Khoa

How to use ScrollViewReader in SwiftUI

Issue #761 Explicitly specify id ScrollView { ScrollViewReader { proxy in LazyVStack(spacing: 10) { ForEach(items) { item in Cell(item: item) .id(item.id) } } .padding() .onReceiveKeyboard(onNext: { onNext() if let item = selectedItem { proxy.scrollTo(item.id, anchor: .center) } }, onPrevious: { onPrevious() if let item = selectedItem { proxy.scrollTo(item.id, anchor: .center) } }) } } I usually extract ScrollViewReader into a helper function that use onChange to react to state changes...

January 21, 2021 · 1 min · 110 words · Khoa

How to handle keyDown in NSResponder

Issue #760 import AppKit import Omnia class MyWindow: NSWindow { override func keyDown(with event: NSEvent) { super.keyDown(with: event) if isKey(NSDeleteCharacter, event: event) { NotificationCenter.default.post(Notification(name: .didKeyboardDeleteItem)) } else if isKey(NSUpArrowFunctionKey, event: event) { print("up") } else if isKey(NSDownArrowFunctionKey, event: event) { print("down") } else if isKey(NSLeftArrowFunctionKey, event: event) { print("left") } else if isKey(NSRightArrowFunctionKey, event: event) { print("right") } } private func isKey(_ key: Int, event: NSEvent) -> Bool { if let scalar = UnicodeScalar(key) { return event....

January 21, 2021 · 2 min · 268 words · Khoa

How to handle NSSearchToolbarItem in macOS 11

Issue #758 extension NSToolbarItem.Identifier { static let searchItem: NSToolbarItem.Identifier = NSToolbarItem.Identifier("SearchItem") } let searchItem = NSSearchToolbarItem(itemIdentifier: .searchItem) extension AppDelegate: NSToolbarDelegate { func toolbar( _ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool ) -> NSToolbarItem? { switch itemIdentifier { case .searchItem: searchItem.searchField.delegate = self return searchItem } } extension AppDelegate: NSSearchFieldDelegate { func control( _ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector ) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { print("enter") return true } return false } }

January 21, 2021 · 1 min · 79 words · Khoa

How to do launch at login for macOS apps

Issue #757 Use SMLoginItemSetEnabled from Service Management framework Use a helper background app that checks and invokes our main application Copy our helper app into Library/LoginItems helper_dir="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Library/LoginItems" final class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { let bundleId = Bundle.main.bundleIdentifier! // TODO: Make this more strict by only replacing at the end let mainBundleId = bundleId.replacingOccurrences(of: "-LaunchAtLoginHelper", with: "") // Ensure the app is not already running guard NSRunningApplication....

January 20, 2021 · 1 min · 98 words · Khoa

How to fix overlapped navigation titles in SwiftUI

Issue #756 extension NavigationLink { func fixOverlap() -> AnyView { if UIDevice.current.userInterfaceIdiom == .phone { return self.isDetailLink(false).erase() } else { return self.erase() } } } Read more https://www.dabblingbadger.com/blog/2020/12/11/a-quick-fix-for-overlapping-navigation-titles-in-swiftui As far as I can tell, this bug only shows up if you: 1) have the navigation title displayMode of a destination view set to .large and 2) have added items to the navigation bar using the .navigationBarItems modifier.

January 20, 2021 · 1 min · 67 words · Khoa

How to use NSWindow style and NSToolbar in AppKit

Issue #755 Read more https://github.com/martinlexow/SwiftUIWindowStyles https://github.com/lukakerr/NSWindowStyles https://github.com/marioaguzman/toolbar

January 20, 2021 · 1 min · 7 words · Khoa

How to use NSSplitViewController in AppKit

Issue #754 Read more https://github.com/KevinGutowski/SplitConfigurations

January 20, 2021 · 1 min · 5 words · Khoa

How to add alternative app icons for iOS

Issue #749 Some apps want to support alternative app icons in Settings where user can choose to update app icon. Here’s some must do to make it work, as of Xcode 12.2 In Info.plist, must declare CFBundleIcons with both CFBundlePrimaryIcon and CFBundleAlternateIcons Icons must be in project folder, not Asset Catalog Here’s how it is done on my app PastePal Add app icons to project folder Prepare icons, like Icon1, Icon2 with 2 variants for 2x and 3x....

January 14, 2021 · 2 min · 388 words · Khoa

How to make popup button in SwiftUI for macOS

Issue #748 There is said to be PopUpButtonPickerStyle and MenuPickerStyle but these don’t seem to work. There’s Menu button it shows a dropdown style. We fake it by fading this and overlay with a button. allowsHitTesting does not work, but disabled seems to do the trick Menu { Button("About", action: ActionService.onAbout) Button("Quit", action: ActionService.onQuit) } label: { Text("") } .frame(width: 24) .opacity(0.01) .overlay( makeButton(action: {}, "gearshape.fill") .disabled(true) .foregroundColor(Color.secondaryLabel) ) Follow pika...

January 13, 2021 · 1 min · 202 words · Khoa

How to use UITextView in SwiftUI

Issue #747 Need to use Coordinator conforming to UITextViewDelegate to apply changes back to Binding import SwiftUI import UIKit struct MyTextView: UIViewRepresentable { @Binding var text: String final class Coordinator: NSObject, UITextViewDelegate { let parent: MyTextView init(parent: MyTextView) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { if textView.text != parent.text { parent.text = textView.text } } } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeUIView(context: Context) -> UITextView { let view = UITextView() view....

January 8, 2021 · 1 min · 108 words · Khoa

How to check app going to background in SwiftUI

Issue #746 From iOS 13, the default is to support multiple scene, so the the old UIApplicationDelegate lifecycle does not work. Double check your Info.plist for UIApplicationSceneManifest key <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> </dict> One way to be notified about application life cycle is to use UIApplicationDelegateAdaptor and via NotificationCenter import SwiftUI import UIKit import FontAwesomeSwiftUI final class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FontAwesome....

January 7, 2021 · 1 min · 110 words · Khoa

How to use selection in List in SwiftUI

Issue #745 I used to use selection with Binding where wrappedValue is optional, together with tag in SwiftUI for macOS, and it shows current selection @Binding var selection: Tag? = .all List(section: $selection) { Text("All") .tag(Tag.all) } From the API, looks like Binding<Set> is for multiple selection, and Binding<Optional> is for single selection Looking at List signature, I see that selection uses wrappedValue as Set for Binding<Set<SelectionValue>>? init<Data, ID, RowContent>(Data, id: KeyPath<Data....

January 6, 2021 · 1 min · 129 words · Khoa

How to simplify communication patterns with closure in Swift

Issue #744 As someone who builds lots of apps, I try to find quick ways to do things. One of them is to avoid repetitive and cumbersome APIs. That’s why I built Anchors to make Auto Layout more convenient, Omnia to add missing extensions. The next thing in the itchy list is the many ways to communicate among objects in iOS and macOS development that can be very annoying sometimes....

January 6, 2021 · 11 min · 2333 words · Khoa