How to clear background for TextField inside list in macOS

Issue #986 When using TextField in SwiftUI List on Mac, it has unwanted background color when focused. We can turn it off using introspection or a custom TextField wrapper TextField("Search", text: $searchText) .introspect(.textField, on: .macOS(.v14, .v15)) { $0.drawsBackground = true $0.backgroundColor = .clear }

November 5, 2024 · 1 min · 44 words · Khoa

How to conditionally render widgets in iOS

Issue #984 The WidgetBundle lets us expose multiple widgets from a single widget extension It uses WidgetBundleBuilder to constructs a widget bundle’s body. In iOS 18, if we include ControlWidget then we need to check iOSApplicationExtension iOS 18. For now in Xcode 16 there’s a bug that prevents existing widgets from appearing in iOS 17. We can leverage WidgetBundleBuilder to conditionally render widgets for each iOS version import SwiftUI import WidgetKit import Widgets @main struct OurAppWidgetBundle: WidgetBundle { var body: some Widget { if #available(iOSApplicationExtension 18....

September 25, 2024 · 1 min · 123 words · Khoa

How to open app with Control Widget on iOS 18

Issue #983 In iOS 18, we can make Control Widget in Widget extension import WidgetKit import SwiftUI @available(iOS 18.0, *) struct BookControlWidget: ControlWidget { var body: some ControlWidgetConfiguration { StaticControlConfiguration(kind: "Book") { ControlWidgetButton(action: BookIntent()) { Label("Book", systemImage: "airplane") } } .displayName("Book") } } import AppIntents @available(iOS 18.0, *) struct BookIntent: AppIntent { static let title: LocalizedStringResource = "Book" static var openAppWhenRun = true static var isDiscoverable = true func perform() async throws -> some IntentResult & OpensIntent { let url = URL(string: "myapp://book")!...

September 25, 2024 · 2 min · 389 words · Khoa

How to make NSCollectionView with diffable data source and SwiftUI

Issue #980 NSCollectionView, available since macOS 10.5+, is a good choice to present a list of content. Let’s make a SwiftUI wrapper for NSCollectionView with diffable data source and compositional layout Use NSViewControllerRepresentable First, let’s create a SwiftUI view that will represent the macOS view controller. We’ll use the NSViewControllerRepresentable protocol to bridge SwiftUI and AppKit. import SwiftUI import AppKit struct CollectionView: NSViewControllerRepresentable { func makeNSViewController(context: Context) -> CollectionViewController { return CollectionViewController() } func updateNSViewController(_ nsViewController: CollectionViewController, context: Context) { nsViewController....

August 15, 2024 · 3 min · 627 words · Khoa

How to handle tap gesture in SwiftUI Charts

Issue #978 From iOS 17, SwiftUI Charts has chartGesture, together with SpatialTapGesture we can check tap location and convert that to Charts value Chart {} .chartGesture { chart in SpatialTapGesture() .onEnded { value in let result = chart.value(at: value.location, as: (Int, Double).self) if let index = result?.0, let log = logs[safe: index] { print(log) } } }

August 7, 2024 · 1 min · 57 words · Khoa

How to make attributed TextView for macOS and iOS with SwiftUI

Issue #956 macOS import Foundation import SwiftUI import AppKit struct AttributedTextView: NSViewRepresentable { @Binding var attributedText: NSAttributedString var isEditable: Bool = true final class Coordinator: NSObject { let parent: AttributedTextView init( parent: AttributedTextView ) { self.parent = parent super.init() } } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeNSView(context: Context) -> NSScrollView { let view = NSTextView.scrollableTextView() if let textView = view.documentView as? NSTextView { textView.font = NSFont.preferredFont(forTextStyle: ....

December 7, 2023 · 2 min · 257 words · Khoa

How to show anchor bottom view in SwiftUI

Issue #954 From iOS 15, there’s a handy safeAreaInset that allows us to place additional content extending the safe area. Shows the specified content beside the modified view. safeAreaInset allows us to customize which edge and alignment we can place our views. This works for both ScrollView, List and Form and you can apply it multiple times. The content view is anchored to the specified horizontal edge in the parent view, aligning its vertical axis to the specified alignment guide....

November 22, 2023 · 2 min · 218 words · Khoa

How to drag multiple files in SwiftUI on Mac

Issue #951 Create a custom NSView that handles mouseDragged to beginDraggingSession struct DraggingSourceViewWrapper: NSViewRepresentable { let fileUrls: [URL] let onEnd: () -> Void func makeNSView(context: Context) -> DraggingSourceView { let view = DraggingSourceView(fileUrls: fileUrls) view.onEnd = onEnd return view } func updateNSView(_ view: DraggingSourceView, context: Context) { view.onEnd = onEnd } } final class DraggingSourceView: NSView { let fileUrls: [URL] var onEnd: (() -> Void)? init(fileUrls: [URL]) { self.fileUrls = fileUrls super....

November 13, 2023 · 2 min · 237 words · Khoa

How to store Codable in AppStorage

Issue #949 AppStorage and SceneStorage accepts RawRepresentable where value is Int or String. Creates a property that can read and write to a string user default, transforming that to RawRepresentable data type. init(wrappedValue:_:store:) init( wrappedValue: Value, _ key: String, store: UserDefaults? = nil ) where Value : RawRepresentable, Value.RawValue == String One clever thing (that does not work) is to use a custom Codable type that conforms to RawRepresentable, like below...

October 3, 2023 · 2 min · 333 words · Khoa

How to update widget for iOS 17

Issue #948 iOS 17 has a new Stand by mode so SwiftUI introduces containerBackground for the system to decide when to draw background. It also automatically applies margin to widget so we may need to disable that To update existing widgets, we can write some useful extension extension View { @ViewBuilder func safeContainerBackground(@ViewBuilder content: () -> some View) -> some View { if #available(iOS 17.0, *) { self.containerBackground(for: .widget, content: content) } else { self....

October 2, 2023 · 1 min · 155 words · Khoa

How to make tag flow layout using Layout protocol in SwiftUI

Issue #944 SwiftUI in iOS 16 supports Layout protocol to arrange subviews We need to implement 2 methods sizeThatFits(proposal:subviews:cache:) reports the size of the composite layout view. placeSubviews(in:proposal:subviews:cache:) assigns positions to the container’s subviews. We will make a custom layout that arranges elements from top leading and span element to new rows if it can’t fit the current row. Start by defining a FlowLayout struct that conforms to Layout protocol. We will define a helper Arranger to do common arrangement logic....

August 22, 2023 · 3 min · 483 words · Khoa

How to use hover annotation in Swift Charts

Issue #943 In this tutorial, we’ll learn how to use Swift Charts to visualize ranking data. We use default AxisMarks and AxisMarks to let Swift Charts interpolate x and y grid lines. For y axis, I want to have finer grain control over the grid, so I specify an arrayLiteral Note that for rank, the lower value the better ranking, so we make use of chartYScale with reversed automatic domain to flip from 0 -> 100 to 100 -> 0...

August 18, 2023 · 2 min · 262 words · Khoa

How to scale image fill without affect layout in SwiftUI

Issue #939 Instead of letting the Image decide the size, we can put it as background or overlay. I use clipped and contentShape to avoid the fill image obscuring touch event Color.clear .frame(height: 100) .overlay { AsyncImage(url: item.imageUrl) { image in image .resizable() .scaledToFill() } placeholder: { ProgressView() } } .clipped() .contentShape(Rectangle())

July 30, 2023 · 1 min · 52 words · Khoa

How to use AppIntents in iOS 16

Issue #930 AppIntents Declare AppShortcutsProvider, note that appShortcuts uses @AppShortcutsBuilder syntax import AppIntents struct OurShortcutsProvider: AppShortcutsProvider { static var shortcutTileColor: ShortcutTileColor = .lightBlue @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { AppShortcut(intent: AskIntent(), phrases: [ "Hey Joy", "Ask \(.applicationName)" ]) } } We can create an app intent in code import AppIntents import OpenAI struct AskIntent: AppIntent { static var title: LocalizedStringResource = "Hey Joy" static var description: IntentDescription = "Ask me anything" @Parameter(title: "Prompt") var prompt: String?...

July 11, 2023 · 1 min · 195 words · Khoa

How to press and hold button in SwiftUI

Issue #929 We can use ButtonStyleConfiguration to detect isPressed state struct RecordButton: View { var body: some View { Button { } label: { Image(systemSymbol: .micFill) } .buttonStyle(RecordButtonStyle()) } } private struct RecordButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .background { if configuration.isPressed { Circle() .fill(Color.pink) .frame(square: 30) } } .onChange(of: configuration.isPressed) { isPressed in print("isPressed", isPressed) } } } Alternatively, you can just use a DragGesture...

July 10, 2023 · 1 min · 147 words · Khoa

How to use NavigationSplitView and NavigationStack in SwiftUI

Issue #924 Note Navigation state needs to be in the container of NavigationSplitView for changes to propagate Need to use WindowGroup for navigation bar to work NavigationSplitView the navigation split view coordinates with the List in its first column, so that when people make a selection, the detail view updates accordingly. Programmatic changes that you make to the selection property also affect both the list appearance and the presented detail view...

June 30, 2023 · 1 min · 161 words · Khoa

How to style NavigationLink in macOS

Issue #923 NavigationLink on Mac applies the default button style. We can style it using ButtonStyle, here to use plain style we can just NavigationLink(value: DetailRoute.books) { BooksView() } .buttonStyle(.plain)

June 28, 2023 · 1 min · 30 words · Khoa

SwiftUI EnvironmentValues

Issue #922 EnvironmentValues Views in SwiftUI can react to configuration information that they read from the environment using an Environment property wrapper Updated for iOS 17

June 21, 2023 · 1 min · 26 words · Khoa

How to make TextField Stepper in SwiftUI

Issue #921 Use HStack with TextField and a little extension extension Binding where Value == Int { var toString: Binding<String> { Binding<String>( get: { "\(wrappedValue)" }, set: { wrappedValue = Int($0) ?? 0 } ) } } struct TextFieldStepper: View { @Binding var value: Int var body: some View { HStack(spacing: 0) { TextField("", text: $value.toString) .textFieldStyle(.roundedBorder) .frame(width: 50) Stepper("", value: $value) } } }

June 20, 2023 · 1 min · 65 words · Khoa

How to clear TextEditor background in SwiftUI

Issue #920 For iOS 16 and macOS 13.0 TextEditor(text: $text) .scrollContentBackground(.hidden) For below, use [SwiftUI-Introspect](https://github.com/siteline/SwiftUI-Introspect) TextEditor(text: $text) .instrospectTextView { $0.drawsBackground = false }

June 19, 2023 · 1 min · 23 words · Khoa