Hi,
If Im in detailsOnly mode in SplitView how to manually open the SideBar as popover same as behavior as default rectangle menu icon at top of Details View.
Kind Regards
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
ExampleCode.txt
Dear all,
I made an app for computing Finite Element Analysis of electric motors. I (think I) managed to follow the MVVM principle by not exposing the model to the views. Now, my goal is to be able to use documents, each representing a different motor. I want to have my files saved on iCloud, and I want to be able to read plain text from it, so some other code (i.e. python) can create new configurations, even though this app is made for building, graphically.
Before trying to work with FileDocument, my class ViewModel: ObservableObject had properties with Published, like @Published var staOD = 80.0, and I would have views with TextFields to change these values.
Now, I’m trying to blend in FileDocument, and I’m lost. I don’t know how I should work with my data. Under the “Separation of Concerns”, I guessed that:
ViewModel: Should handle computations, updates, and application logic.
Document: Should focus on data persistence and encapsulate data to be saved/loaded as a document.
My ViewModel looks a bit strange to me, and I’m not sure I’m updating it the right way. I have around 100 parameters, I’m afraid I’m updating these parameters too often and unnecessarily every parameter at the same time, even when only one value is changed in the document.
What I’m asking:
Clarifications on how to work with FileDocument in my case of MVVM (I’m open to change the entire workflow, my main knowledge is on the Model built in Swift, but not SwiftUI)
Why isn’t the computed area on the DocumentView at the right value when I open a document? I would like to open documents and have it in the “right” state. In reality, I’m computing an image of the electric motor, and it would be nice to open the document and see the “real” image, and not a dummy image before I can validate the geometry.
I have these warnings popping every time I open a document and that scares me, especially because I want ideally to use swift 6 in the coming future, with concurrency the right way.
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
Thanks a lot for your help,
Guillaume
I made an example/simplified code that has all the logic. I can't show the entire code in this prompt due to space limitation. Therefore, I put everything (184 lines) in a single Swift file for you to download. You can just create a Multiplatform Document App. Remove all files except the ...App file, in which you can paste the content of the shared swift file. Run on iPhone simulator.
Development environment: Xcode 16, macOS 15
Run-time configuration: iOS 18, macOS 15
Open the app, click on "New motor"
You will see "Computed area: 3'063..."
Click on "Geometry", change "Stator OD" to 60 instead of 80.
Click on "Save" button, now Computed area is 863...
Click on "Cancel" button, and reopen the same document
Problem: area is again 3'063 while when you open Geometry, you see that "Stator OD" is rightfully 60.
Hi! Seems StateObject isn't being deallocated whilst it's containing View has disappeared successfully.
Suppose overlay is the cause. StateObject hangs until next view body evaluation happens.
import SwiftUI
final class Copmanion: ObservableObject {
deinit {
print("☠️ Copmanion died")
}
}
@MainActor
final class SessionManager: ObservableObject {
@Published
var isActive = false
}
struct CheckPasscodeView: View {
@EnvironmentObject var session: SessionManager
@StateObject var copmanion = Copmanion()
var body: some View {
VStack {
Button("Login") {
session.isActive = true
}
}
.onAppear {
print("CheckPasscodeView appear")
}
.onDisappear {
print("CheckPasscodeView disappear")
}
}
}
struct ContentView: View {
@StateObject var session = SessionManager()
var body: some View {
Color.teal
.overlay {
if !session.isActive {
CheckPasscodeView()
.environmentObject(session)
} else {
EmptyView()
}
}
}
}
ZStack here is one of possible solutions as well as using Observation framework.
Conditions:
Xcode 16
Xcode 16.1 Beta 2
iphoneosSDK 18.1
Topic:
UI Frameworks
SubTopic:
SwiftUI
In this app, I set the preferredColorScheme in main
@main
struct TheApp: App {
@AppStorage("isDarkMode") private var isDarkMode = false
var body: some Scene {
WindowGroup {
ContentView()
.preferredColorScheme(isDarkMode ? .dark : .light)
}
}
}
isDarkMode is changed dynamically in code.
When code is compiled with Xcode 15.3, the background of ContentView changes from light to dark or vice versa.
When compiled with Xcode 16, no more change.
However, direct changes to objects do work, such as:
TextField("N", value: $t, format: .number)
.frame(width: 40)
.background(isDarkMode ? .gray : .white)
What has changed in Xcode 16 ? Is it documented somewhere ?
I saw this SO thread which describe similar problem.
https://www.reddit.com/r/swift/comments/upkprg/preferredcolorscheme_toggle_not_working/
I think I've found a bug with SwiftUI TabView selection binding on Mac Catalyst 18.
This code prints to the console when the SceneStorage variable for the TabView selection changes.
If you run the code below on iOS and change the tab, the selection prints to the console indicating the SceneStorage variable has changed.
If you run the code below on Mac Catalyst 18 in Sequoia and change the tab, the selection does not print to the console indicating the SceneStorage variable has not changed.
Looking for input please before I turn this over to Apple code-level support.
//
// ContentView.swift
// tabview-catalyst-issue
//
import SwiftUI
import os.log
enum TabDisplay: String, CaseIterable, Identifiable {
case list
case grid
var id: String { self.rawValue }
}
struct ContentView: View {
@SceneStorage("selectedTabView") var selectedTabView = TabDisplay.list
var body: some View {
VStack {
TabView(selection: $selectedTabView) {
Text("LIST")
.tabItem {
Image(systemName: "list.bullet")
Text("List")
}
.tag(TabDisplay.list)
Text("GRID")
.tabItem {
Image(systemName: "rectangle.split.3x3")
Text("Grid")
}
.tag(TabDisplay.grid)
}
}
.padding()
.onChange(of: self.selectedTabView, perform: { value in
// Added to show that the TabView binding doesn't work in Mac Catalyst 18
print(value)
})
}
}
#Preview {
ContentView()
}
I encountered a crash when calling the tabBarView:didSelectIndex: method, and it only happens on iOS 18.
Here is crash report:
`OS Version: iPhone OS 18.0.1 (22A3370)
Report Version: 105
SDK Version: 0.0.4
Exception Type: SIGABRT
Exception Codes: #0 at 0x1f0acb274
Crashed Thread: 0
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Layout requested for visible navigation bar, <UINavigationBar: 0x11266ef80; frame = (0 -91; 428 44); autoresize = W; tintColor = UIExtendedSRGBColorSpace 1 1 1 0.8; layer = <CALayer: 0x301e14800>> delegate=0x112670600 standardAppearance=0x3035b3720 scrollEdgeAppearance=0x3035b3b10, when the top item belongs to a different navigation bar. topItem = <UINavigationItem: 0x14309cf00> title='' titleView=0x15b1af700 style=navigator leftBarButtonItems=0x301c08940 rightBarButtonItems=0x301caa180, navigation bar = <UINavigationBar: 0x11266e080; frame = (0 -91; 428 44); autoresize = W; tintColor = UIExtendedSRGBColorSpace 1 1 1 0.8; layer = <CALayer: 0x301e1d0a0>> delegate=0x112670000 standardAppearance=0x30355ae60 scrollEdgeAppearance=0x30355aed0, possibly from a client attempt to nest wrapped navigation controllers.'
Last Exception Backtrace:
0 CoreFoundation 0x00000001a0cad08c 0x1a0c29000 + 540812
1 libobjc.A.dylib 0x000000019dfaf2e4 0x19df98000 + 94948
2 Foundation 0x00000001a007e15c 0x19f85c000 + 8528220
3 UIKitCore 0x00000001a36b6028 0x1a33f4000 + 2891816
4 UIKitCore 0x00000001a3404248 0x1a33f4000 + 66120
5 UIKitCore 0x00000001a354d878 0x1a33f4000 + 1415288
6 QuartzCore 0x00000001a2749630 0x1a26d1000 + 493104
7 UIKitCore 0x00000001a3447cd0 0x1a33f4000 + 343248
8 UIKitCore 0x00000001a34e9a60 0x1a33f4000 + 1006176
9 UIKitCore 0x00000001a354aa48 0x1a33f4000 + 1403464
10 UIKitCore 0x00000001a34f1c64 0x1a33f4000 + 1039460
11 UIKitCore 0x00000001a368e2c8 0x1a33f4000 + 2728648
12 UIKitCore 0x00000001a368c504 0x1a33f4000 + 2721028
13 UIKitCore 0x00000001a34193c8 0x1a33f4000 + 152520
14 UIKitCore 0x00000001a34191f4 0x1a33f4000 + 152052
15 UIKitCore 0x00000001a340ae84 0x1a33f4000 + 93828
16 CoreAutoLayout 0x00000001c3ad7030 0x1c3ac5000 + 73776
17 UIKitCore 0x00000001a340ddc8 0x1a33f4000 + 105928
18 UIKitCore 0x00000001a340c7f0 0x1a33f4000 + 100336
19 UIKitCore 0x00000001a378087c 0x1a33f4000 + 3721340
20 UIKitCore 0x00000001a37804f0 0x1a33f4000 + 3720432
21 UIKitCore 0x00000001a377e814 0x1a33f4000 + 3713044
22 UIKitCore 0x00000001a37ee8a4 0x1a33f4000 + 4171940
23 UIKitCore 0x00000001a37edf14 0x1a33f4000 + 4169492`
Topic:
UI Frameworks
SubTopic:
UIKit
Hello, I am updating my live activity for the new ios18 Smart Stack functionality.
I got it working through the WWDC session (Bring your live activities to Apple watch).
I'm doing
supplementalActivityFamilies([.small])
and then a custom layout in the .small ActivityFamily
However, any images I try to use in the Smart Stack just show up as grey squares. (it works on the phone live activity)
I suspect it's because my app images are not moved over to the watch? Because I don't have a watch app and such no watch target?
If anyone can help me understand if there's anything I can do in order to have a custom image show up on the smart stack, I'd be very grateful.
I use onPreferenceChange() and onScrollPhaseChange() to detect scroll is triggered by double tap. Is there any way to directly know the scroll is triggered by double tap?
I've found another interesting issue with the Focus system.
My text case is SwiftUI in Preview or Simulator (using an iPad mini case) -- I haven't tried this on other environments, so your mileage may vary.
I have a Form including several TextFields. Adding the usual @FocusState logic, I observe that if I type a TAB key, then
(a) If the TextFields are specified using TextField(“label”, text: $text), typing the tab key switches focus from field to field as expected, BUT
(b) If the TextFields are specified using TextField(“label”, text: $text, axis: .vertical), typing the tab key adds whitespace to the field and does NOT change focus.
In the latter case, I need to use an .onKeyPress(.tab) {….} modifier to achieve the desired result.
Reported as FB15432840
Hello, everybody
I have a little problem I've finished learning swift and I've already know some SwiftUI, but I'm not really confident, also I had a break in my learning path. So I'm thinking about starting to learn SwiftUI one more time from zero, but I'm not sure where it can be better to do. My minimal iOS version - 17.
I was thinking about apple tutorials
https://developer.apple.com/tutorials/swiftui
https://developer.apple.com/tutorials/develop-in-swift
But is there any special order (of apple tutorials) or even other better tutorials? What can you recommend from your side?
Thank you in advance
I have an NSMutableAttributedString that contains an NSMutableParagraphStyle, which sets the paragraphSpacing.
When I preview this in UIKit, everything displays correctly (paragraph spacing is applied). However, when I wrap this into an AttributedString, use it in a Text view (SwiftUI), the paragraphSpacing is ignored.
I looked into the documentation and noticed that AttributedString in SwiftUI does not yet support paragraphSpacing. For now, the only workaround I see is to keep this part in UIKit.
Has anyone found a way to make this work in SwiftUI?
I am trying to figure out how to set a button's label icon to match the button style when inside a list.
These four buttons display differently depending on context – if put inside a List or a VStack:
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.automatic)
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.bordered)
Button {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.borderedProminent)
Button(role: .destructive) {} label: {
Label("Test", systemImage: "figure")
}.buttonStyle(.borderedProminent)
Inside a List, which looks weird in my opinion.
For reference, this is what they look like inside a VStack, which is what I'd expect:
I am not sure if this is intentional or a bug. If there are any workaround which do not explicitly set a specific color, like .foreground(.red), please let me know.
When remove intermediate screen from path I expect the same behaviour like removing view controller from UINavigationController.viewControllers in UIKit.
But now it evoke pop animation and recreate associated StateObject.
It looks like instead of remove specific view controller in hierarchy, it removes the last one and change content on previous.
Code example to reproduce (Open Screen1, then Screen2 and touch Pop previous button):
enum Screen: String {
case screen1
case screen2
}
struct ContentView: View {
@State var path: [Screen] = []
var body: some View {
NavigationStack(path: $path) {
VStack {
Text("Root")
Button("open Screen 1") {
path.append(.screen1)
}
}
.navigationDestination(for: Screen.self) {
switch $0 {
case .screen1: ScreenView1(path: $path)
case .screen2: ScreenView2(path: $path)
}
}
}
}
}
struct ScreenView1: View {
@Binding var path: [Screen]
var body: some View {
VStack {
Text("Screen 1")
Button("Pop") { path.removeLast() }
Button("open Screen 2") { path.append(.screen2) }
}
}
}
struct ScreenView2: View {
@Binding var path: [Screen]
@StateObject var viewModel: Screen2ViewModel = .init()
var body: some View {
VStack {
Text("Screen 2")
Button("Pop") { path.removeLast() }
Button("Pop previous") {
// !!! Here the problem
path.remove(at: path.count - 2)
}
.disabled(path.count < 2)
}
}
}
class Screen2ViewModel: ObservableObject {
init() {
print("Screen2ViewModel.init")
}
}
Screencast:
It is possible to avoid pop animation by modifying path in transaction without animation:
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
path.remove(at: path.count - 2)
}
But my question is: it is possible to avoid screen associated StateObject recreating?
When the home screen is in tinted mode in iOS 18, the widget gallery seems to display widgets with the background inset from the edge of the widget (i.e. negative padding).
You can see this behavior in the Apple News widget too, where the full-bleed content outside of the inset is very light. This only happens in the sample gallery, not when the widgets are placed on the home screen.
For my widgets, this outer edge contains important content and the clipping behavior makes the widgets look poorly designed when viewed in the gallery.
Is there any way to turn this behavior off and just show the widget normally in the gallery with no weird inset — the way it will actually display when added to the home screen?
If it matters, my widgets are currently configured with:
.contentMarginsDisabled()
.containerBackground(for: .widget) {
// ... (background color) */
}
I have implemented a sample video editing timeline using SwiftUI and am facing issues. So I am breaking up the problem in chunks and posting issue each as a separate question. In the code below, I have a simple timeline using an HStack comprising of a left spacer, right spacer(represented as simple black color) and a trimmer UI in the middle. The trimmer resizes as the left and right handles are dragged. The left and right spacers also adjust in width as the trimmer handles are dragged.
Problem: I want to keep the background thumbnails (implemented currently as simple Rectangles filled in different colors) in the trimmer stationary as the trimmer resizes. Currently they move along as the trimmer resizes as seen in the gif below. How do I fix it?
import SwiftUI
struct SampleTimeline: View {
let viewWidth:CGFloat = 340 //Width of HStack container for Timeline
@State var frameWidth:CGFloat = 280 //Width of trimmer
var minWidth: CGFloat {
2*chevronWidth + 10
} //min Width of trimmer
@State private var leftViewWidth:CGFloat = 20
@State private var rightViewWidth:CGFloat = 20
var chevronWidth:CGFloat {
return 24
}
var body: some View {
HStack(spacing:0) {
Color.black
.frame(width: leftViewWidth)
.frame(height: 70)
HStack(spacing: 0) {
Image(systemName: "chevron.compact.left")
.frame(width: chevronWidth, height: 70)
.background(Color.blue)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
leftViewWidth = max(leftViewWidth + value.translation.width, 0)
if leftViewWidth > viewWidth - minWidth - rightViewWidth {
leftViewWidth = viewWidth - minWidth - rightViewWidth
}
frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
})
.onEnded { value in
}
)
Spacer()
Image(systemName: "chevron.compact.right")
.frame(width: chevronWidth, height: 70)
.background(Color.blue)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ value in
rightViewWidth = max(rightViewWidth - value.translation.width, 0)
if rightViewWidth > viewWidth - minWidth - leftViewWidth {
rightViewWidth = viewWidth - minWidth - leftViewWidth
}
frameWidth = max(viewWidth - leftViewWidth - rightViewWidth, minWidth)
})
.onEnded { value in
}
)
}
.foregroundColor(.black)
.font(.title3.weight(.semibold))
.background {
HStack(spacing:0) {
Rectangle().fill(Color.red)
.frame(width: 70, height: 60)
Rectangle().fill(Color.cyan)
.frame(width: 70, height: 60)
Rectangle().fill(Color.orange)
.frame(width: 70, height: 60)
Rectangle().fill(Color.brown)
.frame(width: 70, height: 60)
Rectangle().fill(Color.purple)
.frame(width: 70, height: 60)
}
}
.frame(width: frameWidth)
.clipped()
Color.black
.frame(width: rightViewWidth)
.frame(height: 70)
}
.frame(width: viewWidth, alignment: .leading)
}
}
#Preview {
SampleTimeline()
}
Hey, I have an app that user selects wallpaper for iPhone. I want a feature that user can set wallpaper direct from app itself for lock screen and home screen not download the image and manually set the wallpaper. As my research there was a PhotoLibrary api that contains PLWallpaperImageViewController.h which allows you to set wallpaper directly.
Thank You!
Below style case was crash after 17.6.1
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let titleText = UITextView(frame: CGRect(origin: .zero, size: CGSize(width: 82, height: 100)))
titleText.textContainer.maximumNumberOfLines = 2
titleText.textContainer.lineFragmentPadding = 0
titleText.textContainer.lineBreakMode = .byTruncatingTail
titleText.textContainerInset = UIEdgeInsets.zero
let attributedText = NSMutableAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.red])
titleText.attributedText = attributedText
view.addSubview(titleText)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Topic:
UI Frameworks
SubTopic:
UIKit
Hello,
With iOS 18, when NavigationStack is in new TabView, with path parameter containing current navigation state is set, the navigation destination view is pushed twice.
See below with example that pushes twice on iOS 18 but is correct on iOS 17
@MainActor
class NavigationModel: ObservableObject {
static let shared = NavigationModel()
@Published var selectedTab: String
@Published var homePath: [Route]
@Published var testPath: [Route]
}
struct ContentView: View {
@StateObject private var navigationModel: NavigationModel = NavigationModel.shared
var body: some View {
TabView(selection: $navigationModel.selectedTab){
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
.tag("home")
TestView()
.tabItem {
Label("Test", systemImage: "circle")
}
.tag("test")
}
}
}
struct HomeView: View {
@StateObject private var navigationModel: NavigationModel = NavigationModel.shared
var body: some View {
NavigationStack(path: $navigationModel.homePath){
VStack{
Text("home")
NavigationLink(value: Route.test1("test1")){
Text("Go to test1")
}
}
.navigationDestination(for: Route.self){ route in
NavigationModelBuilder.findFinalDestination(route:route)
}
}
}
}
I don't what causes the issue because it works well on iOS 16 and iOS 17. I think the path is somehow reset but I don't why (maybe by the TabView ?)
Note that the bug only occurs with TabView.
Don't really know if it is a TabView bug or if it is on my side.
I filed a feedback with sample project FB14312064
Hello,
I'm developing with SwiftUI and have encountered some bug-like behavior that I haven't been able to resolve, so I'm seeking your assistance.
I'm building my view using TextEditor, ScrollViewReader, and List, and I've noticed a strange behavior. When the TextEditor at the bottom of the view is focused and the keyboard is displayed, if I change the keyboard type to Emoji and then switch back to the language keyboard, the scroll becomes misaligned. Although the TextEditor remains focused, when this bug occurs, the view's offset is reset as if the focus was lost.
Could you help me with this?
Hi
I am using PhotosPicker and SwiftData with iOS17.0.
I released my own app using codes of two above. No problem came up right until I upgraded my iphone to iOS 18.0 17th of September 24. A single post pokes the similar problem. SwiftData and PhotosPikcer. He or She said it was about SwiftData Model Insert something. But, I was able to use other methods that use SwiftData, so insert Model setup isn't my problem.
But when tapping a photo to get a photo from PhotosPicker makes the ui goes down, and navigate back. Weird. iIt doesnt crash but when I tap a photo, the debug message [ERROR] Could not create a bookmark: NSError: Cocoa 4097 "connection to service named com.apple.FileProvider" comes up and the view navigates back.
The selecting a photo itself doesnt include any SwiftData related methods, it only does loadTransferable thing and shows the photo on the screen.
I cannot understand it. it only happened when I upgraded to iOS 18.0. AND Then i debugged the prob with Xcode 16.0 nothing but the unexpected message appears and not many posts are up here or google. Can you help me?
Things I tried:
Check any use of the PhotosPickerItem anywhere else. -> No where.
Use try await loadTransferable
Changed the form of initiating PhotosPikcer View
Debugging every line -> Nothing appeared.
PhotosPicker(selection: $currentImage, matching: .images, photoLibrary: .shared()) {
Text("")
}
.frame(height: 360)
.photosPickerStyle(.inline)
.photosPickerAccessoryVisibility(.hidden, edges: .bottom)
.photosPickerDisabledCapabilities(.selectionActions)
.onChange(of: currentImage) { _, newImage in
// SomeLogic
}
.ignoresSafeArea(edges: .bottom)
.transition(.move(edge: .bottom).combined(with: .opacity))