Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

SwiftUI Documentation

Post

Replies

Boosts

Views

Activity

SwiftUI State not reliable updating
Hello, I have a SwiftUI view with the following state variable: @State private var startDate: Date = Date() @State private var endDate: Date = Date() @State private var client: Client? = nil @State private var project: Project? = nil @State private var service: Service? = nil @State private var billable: Bool = false Client, Project, and Service are all SwiftData models. I have some view content that binds to these values, including Pickers for the client/project/service and a DatePicker for the Dates. I have an onAppear listener: .onAppear { switch state.mode { case .editing(let tt): Task { await MainActor.run { startDate = tt.startDate endDate = tt.endDate client = tt.client project = tt.project service = tt.service billable = tt.billable } } default: return } } This works as expected. However, if I remove the Task & MainActor.run, the values do not fully update. The DatePickers show the current date, the Pickers show a new value but tapping on them shows a nil default value. What is also extremely strange is that if tt.billable is true, then the view does update as expected. I am using Xcode 15.4 on iOS simulator 17.5. Any help would be appreciated.
0
0
38
7h
SwiftUI keyboard shortcuts don't become active until view loads
I have a menu bar extra app that includes a sub-menu for lesser used functions. The sub menu implements .keyboardShortcut for a few of the menu items. When the app is first the active app, the keyboard shortcuts only produce a beep UNTIL the sub menu has been accessed. Once the sub-menu has loaded, the keyboard shortcuts work as expected. I should note that the MenuBarExtra is using the .window display mode, if that's important. The submenu appears with a button press within the MenuBarExtra. Is there a method to expose the keyboard shortcuts to the system before the view has loaded?
1
0
34
9h
NavigationStack with NavigationPath triggers multiple init/deinit of views in stack
Hi all, I've noticed some weird behavior when working NavigationStack paired with a NavigationPath, and I'm wondering if it's by design or perhaps a bug. In short: I'm experiencing that every time I push a new view to the NavigationPath, all the previous views appear to init and deinit, which can cause all sorts of problems if you aren't aware of it happening. It's seems like .navigationDestination(for: ) is run once per item in the path that is given to the NavigationStack. So if you add 3 items it'll run 3 times when adding the third view. But the original views and their state are kept. Is this happening because pushing a view to the stack is seen as a state change? And is it intended? The longer explanation: So I'm developing an app in pure SwiftUI and I'm trying to establish a way of navigating through a router / coordinator. I like that my ViewModels can determine when navigation should happen and not the view. E.g. Normally I'd like to prepare some sort of data that should be transferred to the next view. I've prepared an example project which you can use to check out the issue. It's not a full example of my setup, but it's small enough to show what I'm experiencing. It can be found here: https://github.com/Kimnyhuus/NavigationStackDemo The structure is: Router App RootView AppleView + AppleViewModel BananaView + BananaViewModel PearView + PearViewModel So the Router is an ObservableObject that contains a @Published NavigationPath object + functions for adding / removing to / from stack. I've also added an enum here which defines the destinations that the Router can take an navigate to. RootView is setup with a NavigationStack which is setup with the NavigationPath in the parameter: NavigationStack(path: $router.navPath) { ... } RootView also have the router setup as an EnvironmentObject: .environmentObject(router) This enables the other views to interact with the router and push new views to the stack. Each view is initialized with its corresponding VM. The VMs contain nothing other than init and deinit, a variable containing the initialized id + a @Published num which can be set from the view. This is to keep track of the instances in the prints to the console. Each view can navigate to the two other views. You can try and run the project yourselves, but I've made an example of the inits/deinits that happens here. First, I'm navigating from RootView -> AppleView which is expected. The router prints from func navigate(to destination: Destination) that a view has been pushed to the stack. The RootView prints, when .navigationDestination(for: Destination.self) { ...} is triggered, and it says we're navigating to .apple. And then we see that the AppleVM is inited. All like expected. ||| Router: add to navPath: 1 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 879, num: 0 Then I navigate from AppleView -> BananaView and the weird stuff starts happening. We see that a second view has been added to the stack. BananaVM is inited like we'd expect. But then the previous actions seem to run again but with new instances. ||| Router: add to navPath: 2 ||| NavStack: Destination .banana ||| Init ☀️: BananaViewModel, id: 167, num: 0 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 492, num: 0 Then I navigate from BananaView -> PearView and it's continuing. It's now clear that .navigationDestination(for: Destination.self) { ... } is run once per item in the stack. ||| Router: add to navPath: 3 ||| NavStack: Destination .pear ||| Init ☀️: PearViewModel, id: 436, num: 0 ||| NavStack: Destination .banana ||| Init ☀️: BananaViewModel, id: 292, num: 0 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 434, num: 0 ||| Deinit 🔥: AppleViewModel, id: 492, num: 0 Finally I navigate from PearView to AppleView and it's just piling on. ||| Router: add to navPath: 4 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 738, num: 0 ||| NavStack: Destination .pear ||| Init ☀️: PearViewModel, id: 564, num: 0 ||| NavStack: Destination .banana ||| Init ☀️: BananaViewModel, id: 769, num: 0 ||| Deinit 🔥: BananaViewModel, id: 292, num: 0 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 283, num: 0 ||| Deinit 🔥: AppleViewModel, id: 434, num: 0 Navigating back towards the RootView, you can see that it again inits and deinits different instances of the view models. You’ll notice the original ones with state being deinit’ed where it has a number higher than 0 in “num”. ||| Router: rm navPath: 3 ||| NavStack: Destination .banana ||| Init ☀️: BananaViewModel, id: 222, num: 0 ||| Deinit 🔥: BananaViewModel, id: 769, num: 0 ||| NavStack: Destination .pear ||| Init ☀️: PearViewModel, id: 801, num: 0 ||| Deinit 🔥: PearViewModel, id: 564, num: 0 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 173, num: 0 ||| Deinit 🔥: AppleViewModel, id: 283, num: 0 ||| Deinit 🔥: AppleViewModel, id: 738, num: 0 ||| Router: rm navPath: 2 ||| NavStack: Destination .banana ||| Init ☀️: BananaViewModel, id: 26, num: 0 ||| Deinit 🔥: BananaViewModel, id: 222, num: 0 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 744, num: 0 ||| Deinit 🔥: AppleViewModel, id: 173, num: 0 ||| Deinit 🔥: PearViewModel, id: 801, num: 0 ||| Deinit 🔥: PearViewModel, id: 436, num: 3 ||| Router: rm navPath: 1 ||| NavStack: Destination .apple ||| Init ☀️: AppleViewModel, id: 401, num: 0 ||| Deinit 🔥: AppleViewModel, id: 744, num: 0 ||| Deinit 🔥: BananaViewModel, id: 26, num: 0 ||| Deinit 🔥: BananaViewModel, id: 167, num: 2 ||| Router: rm navPath: 0 ||| Deinit 🔥: AppleViewModel, id: 401, num: 0 ||| Deinit 🔥: AppleViewModel, id: 879, num: 1 What is making NavigationStack / .navigationDestination(for: ) run through all the previous items in the stack, every time the state changes in the NavigationPath? I hope it's not too confusing with all the prints :-) Please let me know if I need to add more info.
0
0
44
10h
How to properly add data files into a SwiftData ModelDocument file package
Hi all, I am working on a macOS/iOS sandboxed app with SwiftUI/SwiftData. The app saves its' data into a ModelDocument file. But I want to also save large binary data files into this file. I create the DocumentGroup scene with: DocumentGroup(editing: Entry.self, contentType: .myDocument) { MainWindowView() } In my MainWindowView, I use @Environment(\.documentConfiguration) private var documentConfiguration to get the URL to the user created ModelDocument file URL. When I need to add large binary file into the ModelDocument file, I call a method in my model: func saveReferencedData(_ data: Data, documentURL: URL?) throws { let logger = Logger(subsystem: "saveReferencedData", category: "Asset") if let documentURL { let referencedFileName = "\(entryIdentifier)_\(assetIdentifier).\(assetType)" let tempFileURL = documentURL.appending(components: referencedFileName) if documentURL.startAccessingSecurityScopedResource() { do { try data.write(to: tempFileURL, options: []) } catch { documentURL.stopAccessingSecurityScopedResource() throw AssetFileOperationError.unableToSaveReferenceFile } self.referencedFileLocation = referencedFileName logger.debug("Successfully saved image data to: \(referenceFileURL)") } documentURL.stopAccessingSecurityScopedResource() } else { logger.debug("ERROR! Unable to save referenced image file because document URL is nil.") } } When this method is called, the data file is saved, but immediately I gets this diablog box: If I click on any of the options in the dialog box, the binary data file is removed. I think this is a permission problem and I am not writing to the ModelDocument file correctly. But I can not find any information online to experiment more. Does anyone have experience with a customized ModelDocument file with more data files written into it? Or if you have any insight or advice, it would be greatly appreciated. Thank you so much for reading.
0
0
32
14h
SwiftUI List OutlineGroup
Hello, I'm having some difficulties trying to customise a SwiftUI list-detail splitview using List and OutlineGroup: The model used is provided in Apple documentation OutlineGroup. The contentView is struct ContentView: View { @State var itemString = String() var body: some View { HSplitView { MyOutLine(title: "MyOutLine", itemString: $itemString) .frame(width: 200, height: 300 , alignment: Alignment(horizontal: .leading, vertical: .top)) .padding() MyView(itemString: itemString) } } } The left view is struct MyOutLine: View { let title:String @Binding var itemString:String @State private var selection: FileItem? var body: some View { List(selection: $selection) { OutlineGroup(data, children: \.children) { item in Text ("\(item.description)") .onTapGesture { selection = item itemString = item.description } .listRowBackground( selection == item ? Color.gray :nil ) } } .listStyle(.sidebar) .onAppear { itemString = "No selection"} } } The right view is: struct MyView: View { let itemString:String var body: some View { VStack{ Spacer() HStack { Spacer() Text(itemString) Spacer() } Spacer() } } } The result works but I have 2 problems: The selection works but the detail view is updated only when I click the text but not when I click the rest of the row. This results in the detail view not being sync with the list selection. I would like to customise the colour of the selected row but listRowBackground does not seem to work. Is there a way to fix this Thank you in advance Jean Marie
1
0
63
19h
CompositorLayer, Metal and Alert
Hello. I am creating an app for APV in Metal. I have a CompositorLayer that launches a thread. What can I do to detect the environment alert to stop the thread until it is accepted by the user? If I don't stop the thread, I have problems with the hand tracking alert. Thank you.
1
0
66
23h
Help, can't use TabView
Hi, I learned swift only a few weeks ago, and im trying to make a TabView in Xcode. But, it shows only one item! I've already made this work in other apps, but I can't get it working here! import SwiftUI struct tabs: View { var body: some View { TabView { ContentView() .tabItem { Image(systemName: "house.circle.fill") Text("Home") Settngs() .tabItem { Image(systemName:"gear.circle.fill") Text("Settings") } } } } } #Preview { tabs() } and also, I checked "settngs()" and its my name
1
0
77
1d
Is it possible to get a swiftUI timer-text to align .trailing?
Please consider simple example below. I am trying to put a timer in the upper right corner of a live activity. I am done, it works, but I'm trying to get the timer to look better. If I take a regular text, I can get it to align properly by adjusting the .frame(), , but for a text with a timer inside it, alignment is ignored from what I can see. Text("Hello").frame(width: 90, alignment: .trailing).border(.red) /*Text(timerInterval: timeRange, countsDown: false) .monospacedDigit().font(.subheadline).frame(width: 90, alignment: .trailing).border(.red)*/ } Is there any way to fix this? Right now, I have a fixed width so that HH:mm:ss will fit, but that doesn't look super great if it's just minutes and seconds for example, there's an empty block to the right
1
0
57
1d
NavigationLink/Picker
I have a form container with a Navigation Stack at the top level. In a section I have a Navigation link followed by Picker(s) . On its own the navigation link works correctly but when a HStack with Text and Picker is added the navlink breaks . When the link is selected the picker list is presented. It seems others have had this issue which is usually solved by adding the NavigationStack , which is already at the top level. Any guidance is appreciated ( or work around - I added a section between the link and picker but that didn't help ) Joel
5
0
68
1d
Saving edited PDFs directly to application from QuickLook
I'm stuck on a problem where I need to be able to have the same editing capabilities as in .quickLookPreview and be able to save the edited file to the application with the "Done" button. So, in nutshell, I need to implement the same functionality many other applications provide including Apple's Files. However with .quickLookPreview I don't get the ability to save edited files directly to the application, and I've had no luck finding help from the internet (thus this question). Perhaps somebody has implemented this before and could give me a lead somewhere? PS. I'm trying to find a solution without any third party libraries
0
0
37
1d
Can I preview "regular" view in widget extension?
Basically, in my widget/live activity, I want to extract reusable views into a separate file with an isolated view and preview. Dummy example below. I cannot do it because it says "missing previewcontext". The only way I've found is to add the view to my main app target, but I don't want to clutter my main app wiews that only exist in my widgets if I can avoid it. Can this be done somehow? Thoughts appreciated. Dummy example (tried with and without "previewLayout": struct StatusActivityView: View { let status: UserStatusData var body: some View { VStack(alignment: .center) { Text("Dummy example") }.background(.blue).padding(5) } } @available(iOS 16.2, *) struct StatusActivityView_Previews: PreviewProvider { static var previews: some View { let status = WidgetConstants.defaultEntry() return StatusActivityView(status: status).previewLayout(.sizeThatFits) } }
2
0
62
2d
iOS DisclosureGroup content clipping
I have a SwiftUI page that I want to simplify by showing basic information by default, and putting the additional info behind a "Details" DisclosureGroup for advanced users. I started by laying out all the components and breaking things into individual Views. These all are laid out and look fine. Then I took several of them and added them inside a DisclosureGroupView. But all of a sudden, the views inside started getting crunched together and the contents of the DisclosureGroup got clipped about 2/3 of the way down the page. The problem I'm trying to solve is how to show everything inside the DIsclosureGroup. The top-level View looks like this: VStack { FirstItemView() SecondView() DetailView() // <- Shows disclosure arrow } Where DetailView is: struct DetailView: View { @State var isExpanded = true var body: some View { GeometryReader { geometry in DisclosureGroup("Details", isExpanded: $isExpanded) { ThirdRowView() Spacer() FourthRowView() VStack { FifthRowWithChartView() CaptionLabelView(label: "Third", iconName: "chart.bar.xaxis") } } } } } The FifthRowWithChartView is half-clipped. One thing that might contribute is that there is a Chart view at the bottom of this page. I've tried setting the width and height of the DisclosureGroup based on the height returned by the GeometryReader, but that didn't do anything. This is all on iOS 17.6, testing on an iPhone 15ProMax. Any tips or tricks are most appreciated.
2
0
76
2d
macOS SwiftUI Sheets are no longer resizable (Xcode 16 beta2)
For whatever reason SwiftUI sheets don't seem to be resizable anymore. The exact same code/project produces resizable Sheets in XCode 15.4 but unresizable ones with Swift included in Xcode 16 beta 2. Tried explicitly providing .fixedSize(horizontal false, vertical: false) everywhere humanly possible hoping for a fix but sheets are still stuck at an awkward size (turns out be the minWidth/minHeight if I provide in .frame).
4
1
93
2d
Inconsistency on view lifecycle events between UIKit and SwiftUI when using UIVPageViewController
Overview I've found inconsistency on view lifecycle events between UIKit and SwiftUI as the following shows when using UIVPageViewController and UIHostingController as one of its pages. SwiftUI View onAppear is only called at the first time to display and never called in the other cases. UIViewController viewDidAppear is not called at the first time to display, but it's called when the page view controller changes its page displayed. The whole view structure is as follows: UIViewController (root) UIPageViewController (as its container view) UIHostingController (as its page) SwiftUI View (as its content view) UIViewControllerRepresentable (as a part of its body) UIViewController (as its content) Environment Xcode Version 15.4 (15F31d) iPhone 15 Pro (iOS 17.5) (Simulator) iPhone 8 (iOS 15.0) (Simulator) Sample code import UIKit import SwiftUI class ViewController: UIViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource { private var pageViewController: UIPageViewController! private var viewControllers: [UIViewController] = [] override func viewDidLoad() { super.viewDidLoad() setup() } private func setup() { pageViewController.delegate = self pageViewController.dataSource = self let page1 = UIHostingController(rootView: MainPageView()) let page2 = UIViewController() page2.view.backgroundColor = .systemBlue let page3 = UIViewController() page3.view.backgroundColor = .systemGreen viewControllers = [page1, page2, page3] pageViewController.setViewControllers([page1], direction: .forward, animated: false) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { super.prepare(for: segue, sender: sender) guard let pageViewController = segue.destination as? UIPageViewController else { return } self.pageViewController = pageViewController } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { print("debug: \(#function)") } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { print("debug: \(#function)") guard let viewControllerIndex = viewControllers.firstIndex(of: viewController) else { return nil } let previousIndex = viewControllerIndex - 1 guard previousIndex >= 0, viewControllers.count > previousIndex else { return nil } return viewControllers[previousIndex] } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { print("debug: \(#function)") guard let viewControllerIndex = viewControllers.firstIndex(of: viewController) else { return nil } let nextIndex = viewControllerIndex + 1 guard viewControllers.count != nextIndex, viewControllers.count > nextIndex else { return nil } return viewControllers[nextIndex] } } struct MainPageView: View { var body: some View { VStack(spacing: 0) { PageContentView() PageFooterView() } .onAppear { print("debug: \(type(of: Self.self)) onAppear") } .onDisappear { print("debug: \(type(of: Self.self)) onDisappear") } } } struct PageFooterView: View { var body: some View { Text("PageFooterView") .padding() .frame(maxWidth: .infinity) .background(Color.blue) .onAppear { print("debug: \(type(of: Self.self)) onAppear") } .onDisappear { print("debug: \(type(of: Self.self)) onDisappear") } } } struct PageContentView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { PageContentViewController() } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} } class PageContentViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setup() } private func setup() { view.backgroundColor = .systemYellow let label = UILabel() label.text = "PageContentViewController" label.font = .preferredFont(forTextStyle: .title1) view.addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: view.centerXAnchor), label.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) print("debug: \(type(of: Self.self)) \(#function)") } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) print("debug: \(type(of: Self.self)) \(#function)") } } Logs // Display the views debug: MainPageView.Type onAppear debug: PageFooterView.Type onAppear // Swipe to the next page debug: pageViewController(_:viewControllerAfter:) debug: pageViewController(_:viewControllerBefore:) debug: PageContentViewController.Type viewDidDisappear(_:) debug: pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:) debug: pageViewController(_:viewControllerAfter:) // Swipe to the previous page debug: PageContentViewController.Type viewDidAppear(_:) debug: pageViewController(_:didFinishAnimating:previousViewControllers:transitionCompleted:) debug: pageViewController(_:viewControllerBefore:) As you can see here, onAppear is only called at the first time to display but never called in the other cases while viewDidAppear is the other way around.
0
0
65
2d
TabSection always show sections actions.
I'm giving a go to the new TabSection with iOS 18 but I'm facing an issue with sections actions. I have the following section inside a TabView: TabSection { ForEach(accounts) { account in Tab(account.name , systemImage: account.icon, value: SelectedTab.accounts(account: account)) { Text(account.name) } } } header: { Text("Accounts") } .sectionActions { AccountsTabSectionAddAccount() } I'm showing a Tab for each account and an action to create new accounts. The issue I'm facing is that when there are no accounts the entire section doesn't appear in the side bar including the action to create new accounts. To make matters worse the action doesn't show at all in macOS even when there are already accounts and the section is present in side bar. Is there some way to make the section actions always visible?
0
0
113
4d