How to build container view in SwiftUI

Issue #780 To make a container view that accepts child content, we use ViewBuilder struct ContainerView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { content } } From Swift 5.4, it can synthesize the init, so we can declare resultBuilder for stored property struct AwesomeContainerView<Content: View>: View { @ViewBuilder let content: Content var body: some View { content } }

February 24, 2021 路 1 min 路 Khoa Pham

How to tune performance with ButtonBehavior in SwiftUI

Issue #779 With Xcode 12.4, macOS 11.0 app. Every time we switch the system dark and light mode, the CPU goes up to 100%. Instruments show that there鈥檚 an increasing number of ButtonBehavior Suspect State in a row in LazyVStack Every cell has its own toggle state struct Cell: View { enum ToggleState { case general case request case response } let item: Item @State private var toggleState: ToggleState = ....

February 24, 2021 路 1 min 路 Khoa Pham

How to make simple search bar in SwiftUI

Issue #776 We need to use a custom Binding to trigger onChange as onEditingChanged is only called when the user selects the textField, and onCommit is only called when return or done button on keyboard is tapped. import UIKit import SwiftUI import EasySwiftUI struct SearchBar: View { @Binding var searchText: String let onChange: () -> Void @State private var showsCancelButton: Bool = false var body: some View { return HStack { textField cancelButton } } private var searchTextBinding: Binding<String> { Binding<String>(get: { searchText }, set: { newValue in DispatchQueue....

February 17, 2021 路 1 min 路 Khoa Pham

How to add home screen quick action in SwiftUI

Issue #774 Start by defining your quick actions. You can use UIApplicationShortcutIcon(type:) for predefined icons, or use UIApplicationShortcutIcon(systemImageName:) for SFSymbol enum QuickAction: String { case readPasteboard case clear var shortcutItem: UIApplicationShortcutItem { switch self { case .readPasteboard: return UIApplicationShortcutItem( type: rawValue, localizedTitle: "Read Pasteboard", localizedSubtitle: "", icon: UIApplicationShortcutIcon(type: .add), userInfo: nil ) case .clear: return UIApplicationShortcutItem( type: rawValue, localizedTitle: "Clear Pasteboard", localizedSubtitle: "", icon: UIApplicationShortcutIcon(systemImageName: SFSymbol.wind.rawValue), userInfo: nil ) } } } Add a service to store selected quick action....

February 10, 2021 路 2 min 路 Khoa Pham

How to use EquatableView in SwiftUI

Issue #773 From John Harper 鈥檚 tweet SwiftUI assumes any Equatable.== is a true equality check, so for POD views it compares each field directly instead (via reflection). For non-POD views it prefers the view鈥檚 == but falls back to its own field compare if no ==. EqView is a way to force the use of ==. When it does the per-field comparison the same rules are applied recursively to each field (to choose direct comparison or == if defined)....

February 8, 2021 路 1 min 路 Khoa Pham

How to add new property in Codable struct in SwiftUI

Issue #772 I use Codable structs in my apps for preferences, and bind them to SwiftUI views. If we add new properties to existing Codable, it can鈥檛 decode with old saved json as we require new properties. We can either do cutom decoding with container, but this can result in lots more code and mistakes if we have many properties inside our struct. The quick workaround is to declare new properties as optional, and use a computed property to wrap that....

February 8, 2021 路 1 min 路 Khoa Pham

How to handle escape in NSTextField in SwiftUI

Issue #770 Handle cancelOperation somewhere up in responder chain class MyWindow: NSWindow { let keyHandler = KeyHandler() override func cancelOperation(_ sender: Any?) { super.cancelOperation(sender) keyHandler.onEvent(.esc) } }

February 6, 2021 路 1 min 路 Khoa Pham

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 路 Khoa Pham

How to show multiple popover in SwiftUI

Issue #765 In SwiftUI currently, it鈥檚 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 路 Khoa Pham

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鈥檛 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 路 Khoa Pham

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 路 Khoa Pham

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 路 Khoa Pham

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 路 Khoa Pham

How to make popup button in SwiftUI for macOS

Issue #748 There is said to be PopUpButtonPickerStyle and MenuPickerStyle but these don鈥檛 seem to work. There鈥檚 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 路 Khoa Pham

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 路 Khoa Pham

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 路 Khoa Pham

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 路 Khoa Pham

How to make tiled image in SwiftUI

Issue #737 Use resizingMode of .tile with a tile image from https://www.transparenttextures.com/ Image("transparentTile") .resizable(capInsets: .init(), resizingMode: .tile) .scaleEffect(2) .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity, maxHeight: .infinity) .clipped()

January 2, 2021 路 1 min 路 Khoa Pham

How to use WebView in SwiftUI

Issue #736 struct MyWebView: NSViewRepresentable { let url: URL @Binding var isLoading: Bool func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeNSView(context: Context) -> WKWebView { let view = WKWebView() view.navigationDelegate = context.coordinator view.load(URLRequest(url: url)) return view } func updateNSView(_ nsView: WKWebView, context: Context) { } class Coordinator: NSObject, WKNavigationDelegate { let parent: MyWebView init(parent: MyWebView) { self.parent = parent } func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { parent....

January 2, 2021 路 1 min 路 Khoa Pham

How to use GeometryReader in SwiftUI

Issue #735 From my previous post How to use flexible frame in SwiftUI we know that certain views have different frame behaviors. 2 of them are .overlay and GeometryReader that takes up whole size proposed by parent. By default GeometryReader takes up whole width and height of parent, and align its content as .topLeading struct ContentView_Previews: PreviewProvider { static var previews: some View { VStack { Rectangle() .fill(Color.gray) .overlay( GeometryReader { geo in Text("\(Int(geo....

January 1, 2021 路 1 min 路 Khoa Pham