Using the new navigationDestination and NavigationPath functions previously on iOS 16 everything has been working fine using a custom back button, which calls path.removeLast().
However, if we try this on iOS 17, the screen being removed flashes white.
You can try this code as an example (NOTE THE WHITE FLASH ON REMOVE LAST):
struct DetailView: View {
@Binding var path: NavigationPath
var body: some View {
ZStack {
Color.black
VStack(alignment: .center) {
Spacer()
Button(action: {
path.removeLast()
}, label: {
Text("BACK")
})
Spacer()
}
}
}
}
struct ParentView: View {
@State var path: NavigationPath = .init()
var body: some View {
NavigationStack(path: $path) {
ZStack {
Color.red
VStack(alignment: .center) {
Spacer()
Button(action: {
path.append("TEST")
}, label: {
Text("FORWARD")
})
Spacer()
}
}
.navigationDestination(for: String.self) { _ in
DetailView(path: $path)
}
}
}
}
Any work arounds? Suggestions?
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.
Post
Replies
Boosts
Views
Activity
I built this Hacker News client a couple of months ago, the app works fine in iOS 16 but after I upgraded to iOS 17, the scrollview flitters whenever there's an insertion.
I have tried removing withAnimation block around insertion, and also made sure items in list are identifiable. Anything else I should try?
Replacing LazyVStack with VStack will make the issue go away but LazyVStack is kinda necessary here bc I don't have pagination atm.
screen recording: https://www.reddit.com/link/16hc43h/video/r6feg0l6zxnb1/player
ScrollView {
// other views here
LazyVStack {
ForEach(store.items) { item in
CommentTile(item)
}
}
// other views here
}
Is it possible to somehow support copy/update edit modes when using quickLookPreview modifier, like in QLPreviewController? After pressing 'Done' it only allows me to discard or save to files instead.
Click Button and use swipe to back at pushed screen's left edge, keep finger in screen middle a bit, means don't finish the swipe back gesture, able to see first screen's navigation bar and title already moved. If keep the finger in middle then cancel the gesture, first screen's title and navigation item will be in second screen.
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
NavigationStack {
List {
NavigationLink("NavigationLink") {
Text("Text")
.navigationTitle("Text")
}
}
.navigationTitle("navigationTitle")
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("Button") {
}
}
}
}
}
}
}
I am working on a project where we have a UIViewRepresentable View in the background of a SwiftUI View, and the preferences for the foreground view are getting clobbered.
If I put the UIViewRepresentable View in the foreground (overlay), then preferences on the SwiftUI view are honored. If I use a native SwiftUI View in the background then the preferences are honored.
Consider this code:
import SwiftUI
struct ContentView: View {
var body: some View {
MyView()
.background() {
BackgroundView()
}
.onPreferenceChange(MyPreferenceKey.self) { value in
if let value {
print("-- preference changed: \(value)")
} else {
print("-- preference changed to `nil`")
}
}
}
}
struct MyView: View {
var body: some View {
Text("Hello")
.preference(key: MyPreferenceKey.self, value: "Hello World")
}
}
struct MyPreferenceKey: PreferenceKey {
static func reduce(value: inout String?, nextValue: () -> String?) {}
}
struct BackgroundView: UIViewRepresentable {
func makeUIView(context: Context) -> UIButton {
UIButton()
}
func updateUIView(_ uiView: UIButton, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator {
init() {}
}
}
BackgroundView is a UIViewRepresentable View. In this case the printed output is:
-- preference changed to `nil`
However, if you use .overlay instead of .background:
MyView()
.overlay {
BackgroundView()
}
Then it prints:
-- preference changed: Hello World
Which is what it should. Is there a way to workaround this?
I want to share UserDefaults between main App and macOS Widget, in iOS I can do it using AppGroups, but how to do it between macOS Widget and main App, because macOS widget's AppGroup require to use "Team Identifier" as prefix and main App's AppGroup require prefix to be "group" so how can I share UserDefaults between the two?
We've got hundreds of crashes in our SwiftUI app which we think are "silent" crashes as there are no complaints from clients and yet - it happens on the main thread in the foreground so I'm not completely sure.
The annoying thing is that we have no idea by the stack trace what is causing this issue, I feel helpless as this is causing some very loud noise through management and honestly - myself who wants to have this noise cleared.
this particular crash is the highest impact (one of a few different weird crashes in our app without clear stack trace)
0 SwiftUI 0x895d90 OUTLINED_FUNCTION_2 + 836
1 SwiftUI 0x895da8 OUTLINED_FUNCTION_2 + 860
2 SwiftUI 0x1329880 OUTLINED_FUNCTION_2 + 424
3 SwiftUI 0x6806c OUTLINED_FUNCTION_441 + 584
4 SwiftUI 0x481b0 OUTLINED_FUNCTION_194 + 544
5 UIKitCore 0x1b7194 -[UIViewController removeChildViewController:notifyDidMove:] + 128
6 UIKitCore 0x77d6e8 -[UINavigationController removeChildViewController:notifyDidMove:] + 80
7 UIKitCore 0x205224 -[UIViewController dealloc] + 768
8 UIKitCore 0x1036c -[UINavigationController viewDidDisappear:] + 372
9 UIKitCore 0xd9c4 -[UIViewController _setViewAppearState:isAnimating:] + 1012
10 UIKitCore 0x46e61c -[UIViewController __viewDidDisappear:] + 136
11 UIKitCore 0x7ec024 __64-[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:]_block_invoke_3 + 44
12 UIKitCore 0x1a3f24 -[UIViewController _executeAfterAppearanceBlock] + 84
13 UIKitCore 0x1a3e68 -[_UIAfterCACommitBlock run] + 72
14 UIKitCore 0x1a3d9c -[_UIAfterCACommitQueue flush] + 176
15 UIKitCore 0x1a3ca8 _runAfterCACommitDeferredBlocks + 496
16 UIKitCore 0x3f530 _cleanUpAfterCAFlushAndRunDeferredBlocks + 108
17 CoreFoundation 0x43564 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
18 CoreFoundation 0xabd9c __CFRunLoopDoBlocks + 368
19 CoreFoundation 0x7bbbc __CFRunLoopRun + 856
20 CoreFoundation 0x80ed4 CFRunLoopRunSpecific + 612
21 GraphicsServices 0x1368 GSEventRunModal + 164
22 UIKitCore 0x3a23d0 -[UIApplication _run] + 888
23 UIKitCore 0x3a2034 UIApplicationMain + 340
24 SwiftUI 0x1d1014 OUTLINED_FUNCTION_895 + 2420
25 SwiftUI 0x13216c block_copy_helper.1 + 388
26 SwiftUI 0x11b4bc OUTLINED_FUNCTION_901 + 2868
Number 27 will be our app's Main function and that it - no other trace of our apps code.
Firebase is saying this happens 100% on iOS 16 only and always on the foreground.
How can I get to the bottom of this? how can I debug such a crash?
We have separated much of our UI into different packages to reduce complexity and compile time. When we recently tested using new .xcstrings string catalogs, we hit an unexpected problem.
Strings extracted from SwiftUI components like Text or Button are extracted into the Localizable.xcstrings in the same package, but the default behaviour of Text(_ key:tableName:bundle:comment:) is to use Bundle.main.
When the default behaviour of the string extraction isn't to extract to the main app target, this introduces a very fragile system where it's easy to add code that looks localised, but ends up failing lookup at runtime.
I don't feel comfortable that we will always remember to define the correct module every time we create a Text. Also, other components like Button doesn't have an init that takes a Bundle, so we would also have to remember that Button(_ titleKey:action:) can now only be used in a package if we make sure that the main bundle contains a matching key.
Is there a way for us to make sure that strings are always extracted to the same place as they are resolved against by default? Either by having strings in packages extracted to an xcstrings file in the main app or having Text default to resolving against the module bundle by default?
In my Watch app on watchOS 9 I was using .foregroundColor(myColour) to colour the text in a widgetLabel on a corner complication like this:
let myColour: Color = functionThatReturnsAColorObjectConstructedLike Color.init(...) // green
.widgetLabel {
Text(myText)
.foregroundColor(myColour)
}
It worked fine; the widget label was green.
Now, in watchOS 10, I see that foregroundColor() is being deprecated in favour of foregroundStyle(), and I can use .foregroundStyle(.green), and - importantly - foregroundStyle() is only available on watchOS 10 and newer.
myColour is calculated depending on some other info, so I can't just write .green, and when I use .foregroundStyle(myColour) the widget label comes out as white every time, even if I set myColour = .green.
I think I have to use some sort of extension to pick the right combination, something like:
extension View {
func foregroundType(colour: Colour, style: any ShapeStyle) -> some THING? {
if #available(watchOS 10.0, *) {
return foregroundStyle(style)
} else {
return foregroundColor(colour)
}
}
}
// Usage
let myStyle: any ShapeStyle = SOMETHING?
...
.widgetLabel {
Text(myText)
.foregroundType(colour: myColour, style: myStyle)
It doesn't work. I just can't figure out what should be returned, nor how to return it. Any ideas?
I'm manually placing a subclass of NSView into the parent view using addSubview:positioned:relativeTo. The dirtyRect passed to drawRect: is wildly incorrect. Can folks attempt to reproduce and file bugs? This is awfully close to the release of Sonoma, and I feel like folks with bezier curves (or maybe other drawing code?) in their NSView subclasses are going to experience problems.
To reproduce, place a view (I'm using an NSImageView) as a subview within a view.
Then, create a subclass of NSView and draw a bezier curve in the drawRect method. Add an instance of this subclass as a subview of your original view. I'm offsetting the x value for clarity. When I build with Xcode 15 and run on Ventura or earlier, I get the correct result. Or, if I build with Xcode 14.3 and run on Sonoma I get the correct result. However, when I build in Xcode 15 and run on the RC build of Sonoma, I get a whacky result.
I get something like (origin = (x = -264, y = -146), size = (width = 480, height = 388)) for the dirtyRect in the error case, while the rect is supposed to be (origin = (x = 0, y = 0), size = (width = 48, height = 48)) (I'm basing the frame of the new view on the original image.)
Thanks!
According to UIRequiredDeviceCapabilities documentation https://developer.apple.com/documentation/bundleresources/information_property_list/uirequireddevicecapabilities/, the value iphone-performance-gaming-tier has been added. The description is quoted below.
iphone-performance-gaming-tier
Requires the graphics performance and gaming features equivalent to the iPhone 15 Pro and iPhone 15 Pro Max. Available in iOS 17.0 and later. Unavailable in visionOS.
In Info.plist of Xcode 15.0 (15A240d), setting iphone-performance-gaming-tier is correctly displayed in human readable format.
However, when archiving a build containing this value and submitting it to App Store Connect, I receive an email stating that the bundle is invalid.
The iphone-performance-gaming-tier is not available at this time, although its documentation states that it is available in iOS 17.0 and later. How can I use this value? For example, do I need a special entitlement?
I found an issue when implementing an alert with a TextField to input a name. I want the action button to be disabled until a name has been entered, but the action block is never executed when the button has become enabled and pressed. The problem seems to appear only when name is initially an empty string. Tested with iOS 17.0.
struct MyView: View {
@State private var name = ""
var body: some View {
SomeView()
.alert(...) {
TextField("Name", text: $name)
Button("Action") {
// Action
}.disabled(name.isEmpty)
Button("Cancel", role: .cancel) {}
}
}
}
Our MacOS application has a single window which is occupied by an NSView-derived view. It's been working for the last ten years or so, but when using the Sonoma beta, window updates are badly broken.
We rely on using setNeedsDisplayInRect to redisplay any portions of the view that need to be redisplayed, but no matter how small a rectangle we specify, the entire window is repainted with the background colour before our drawRect implementation is called. We already provide an overload of isOpaque in our view which returns true, and in the past this was effective for suppressing the background fill, but it no longer seems to work (although I can confirm that it is still called along the way).
I've attached an image that shows an example of how a sample window looks after resizing (which is correct) and then what it looks like after using setNeedsDisplayInRect to invalidate the region occupied by the button in the centre. I've explicitly set the NSWindow background colour to blue to make it more obvious :
Is it still possible to inhibit the background fill? Repainting the entire view for every update is not really an option for us, for performance reasons
Hi.
I am very new to SwiftUI and still trying to learn.
I just encountered a very weird problem that I can't figure out.
I have an iPad application that runs on Apple Silicon Macs as "Designed for iPad"; for some reason, this issue only comes up when running it on a Mac, even if the code is exactly the same.
This is the view that's causing issues:
struct ProfileEditView: View {
@EnvironmentObject var mainModel: MainViewModel
@EnvironmentObject var authModel: AuthViewModel
[some other stuff]
var body: some View {
GeometryReader { geo in
VStack(spacing: 20) {
navigationBar()
.padding(.horizontal, 3)
if authModel.user != nil {
VStack(alignment: .leading, spacing: 30) {
PhotosPicker(selection: self.$imageSelection,
matching: .images,
preferredItemEncoding: .compatible) {
ProfilePicView(editIconShown: true)
}
.disabled(authModel.profilePicIsLoading)
.padding(.horizontal, geo.size.width * 0.3)
.padding(.bottom)
VStack(spacing: 10) {
if let error = self.error {
Text(error)
.foregroundStyle(Color.red)
.font(.footnote)
.italic()
}
SettingsTextFieldView(String(localized: "Your name:"),
value: self.$name) {
if nameIsValid {
authModel.updateName(self.name)
}
}
}
Spacer()
actions()
}
.padding(.horizontal, 5)
.padding(.top)
.onAppear {
self.name = authModel.user!.name
}
}
}
.padding()
}
}
}
Obviously, I am injecting the ViewModels instances at the app entry point:
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
@StateObject var authViewModel: AuthViewModel = AuthViewModel()
@StateObject var mainViewModel: MainViewModel = MainViewModel()
@StateObject var statsViewModel: StatsViewModel = StatsViewModel()
@StateObject var purchaseViewModel: PurchaseViewModel = PurchaseViewModel()
@State var revenueCatIsConfigured: Bool = false
init() {
// MARK: Firebase configuration
FirebaseConfiguration.shared.setLoggerLevel(.min)
FirebaseApp.configure()
// MARK: RevenueCat configuration
if let uid = AuthViewModel.getLoggedInUserID() {
Purchases.logLevel = .error
Purchases.configure(withAPIKey: "redacted", appUserID: uid)
self.revenueCatIsConfigured = true
}
}
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(authViewModel)
.environmentObject(mainViewModel)
.environmentObject(statsViewModel)
.environmentObject(purchaseViewModel)
}
}
}
And lastly, ProfileEditView that is causing issues, is a subview of MainView:
MainView has a switch statement, based on the selected tab; one of the possible views is SettingsView, which can show ProfileEditView as a sheet modifier.
For some reason, only when running on mac, I get the error
Thread 1: Fatal error: No ObservableObject of type AuthViewModel found. A View.environmentObject(_:) for AuthViewModel may be missing as an ancestor of this view.
This will come up every time I reference authModel in this view alone.
Both the iPad and iPhone versions work just fine, and again, the code is exactly the same, since it's a "Designed for iPad".
I don't even know how to troubleshoot the issue.
Hello! I've been digging into this for a little bit now, and am hitting something of a wall. Our app is crashing occasionally, and googling the crash yields literally 0 results.
Tl;dr: Something related to adaptivePresentationStyle(for:traitCollection:) is resulting in our app crashing.
Context
In our app, we have a custom UIPresentationController that we use to present a small sheet of content overlaying other app content - similar to a UISheetPresentationController with a medium-ish size. So we have a custom UIViewController to present, and it conforms to the UIAdaptivePresentationControllerDelegate protocol. We also have custom present and dismiss animators.
The crash
However, we seem to be running into a really odd crash. I've attached a crash report as well, but here's what one sees in xcode on reproducing the crash:
The _computeToEndFrameForCurrentTransition block is nil inside the _transitionViewForCurrentTransition block, value of outerStrongSelf currently : <XxxYyyyyyZzz.PopupPresentationController: 0x12d017a40>. This most likely indicates that an adaptation is happening after a transtion cleared out _computeToEndFrameForCurrentTransition. Captured debug information outside block: presentationController : <XxxYyyyyyZzz.PopupPresentationController: 0x12d017a40> presentedViewController : <XxxYyyyyyZzz.SomeViewController: 0x12d03a690> presentingViewController : <UINavigationController: 0x12a817400>
2023-09-25_08-02-33.6523_-0500-7d355cd4a86427213389765ef070a777c4b4aaa3.crash
Whenever we present one of these view controllers, things work just fine. When we try to present another one though, if someone is aggressively changing the phone orientation, the app crashes. This crash occurs somewhere in between dismissing the old VC and presenting the new one. It occurs before I ever hit any breakpoints in the "present" animator for the second view controller.
I've narrowed things down a bit, and by commenting out our implementation of adaptivePresentationStyle(for:traitCollection:), the crash can't be reproduced anymore. The downside there being that the app no longer functions how we want it to. Our definition of that function (which causes crashes) looks like this:
public func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
guard forceUsingFullScreenIfCompact else {
return .none
}
return traitCollection.verticalSizeClass == .compact ? .overFullScreen : .none
}
A bit more testing, and it seems like if that function returns the same thing consistently, nothing crashes. Are we not allowed to put conditional logic in this function?
In the crash, we can see that it occurs due to a failing assertion internal to UIPresentationController:
Last Exception Backtrace:
0 CoreFoundation 0x1afa28cb4 __exceptionPreprocess + 164 (NSException.m:202)
1 libobjc.A.dylib 0x1a8abc3d0 objc_exception_throw + 60 (objc-exception.mm:356)
2 Foundation 0x1aa1b2688 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 172 (NSException.m:251)
3 UIKitCore 0x1b1d05610 __80-[UIPresentationController _initViewHierarchyForPresentationSuperview:inWindow:]_block_invoke + 2588 (UIPresentationController.m:1594)
4 UIKitCore 0x1b21f1ff4 __56-[UIPresentationController runTransitionForCurrentState]_block_invoke_3 + 300 (UIPresentationController.m:1228)
The question
This all leads us to wonder if we're doing something wrong, or if there could be a bug in one of the iOS APIs that we're consuming. Thus, posting here. Does anyone have any insight into how this could be occurring, or has seen this before and has ideas? Thanks!
Miscellaneous info
We target iOS 14+
We've seen this issue in debug and release builds from Xcode 14 and 15
We see the issue on users with iOS 15+, though it could be occurring on 14 in just incredibly low numbers
Using the screencapture CLI on macOS Sonoma 14.0 (23A344) results in a 72dpi image file, no matter if it was captured on a retina display or not.
For example, using
screencapture -i ~/Desktop/test.png in Terminal lets me create a selective screenshot, but the resulting file does not contain any DPI metadata (checked using mdls ~/Desktop/test.png), nor does the image itself have the correct DPI information (should be 144, but it's always 72; checked using Preview.app).
I noticed a (new?) flag option, -r, for which the documentation states:
-r Do not add screen dpi meta data to captured file.
Is that flag somehow automatically set? Setting it myself makes no difference and obviously results in a no-dpi-in-metadata and wrong-dpi-in-image file.
The only two ways I got the correct DPI information in a resulting image file was using the default options (forced by -p): screencapture -i -p, and by making the capture go to the clipboard screencapture -i -c. Sadly, I can't use those in my case.
Feedback filed: FB13208235
I'd appreciate any pointers,
Matthias
I have enabled “StrictConcurrency” warnings in my project that uses SwiftUI. I have a Commands struct. It has a Button, whose action is calling an async method via Task{}. This builds without warnings within Views, but not Commands. There the compiler reports “Main actor-isolated property 'body' cannot be used to satisfy nonisolated protocol requirement”.
Looking at SwiftUI:
In View, body is declared @MainActor:
@ViewBuilder @MainActor var body: Self.Body { get }
In Commands, body is not declared @MainActor:
@CommandsBuilder var body: Self.Body { get }
So the common practice of making a Button action asynchronous:
Button {
Task { await model.load() }
} label:{
Text("Async Button")
}
will succeed without warnings in Views, but not in Commands. Is this intentional? I've filed FB13212559. Thank you.
This function on NSTextLayoutManager has the following signature
func enumerateTextSegments(
in textRange: NSTextRange,
type: NSTextLayoutManager.SegmentType,
options: NSTextLayoutManager.SegmentOptions = [],
using block: (NSTextRange?, CGRect, CGFloat, NSTextContainer) -> Bool
)
The documentation here doesn't define what the CGRect and CGFloat passed to block are. However, looking at sample code Using TextKit2 To Interact With Text, they seem to be the frame for the textsegment and baselineposition respectively.
But, the textSegmentFrame seems to start at origin.x = 5.0 when text is empty. Is this some starting offset for text segments? I don't seem to be able to find mention of this anywhere.
I can set color of the SF symbol image in the application window but cannot do the same in the menu bar. I wonder how I can change the color in the menu?
import SwiftUI
@main
struct ipmenuApp: App {
var body: some Scene {
MenuBarExtra {
Image(systemName: "bookmark.circle.fill")
.renderingMode(.original)
.foregroundStyle(.red)
} label: {
Image(systemName: "bookmark.circle.fill")
.renderingMode(.original)
.foregroundStyle(.red)
}
}
}
xcodebuild -version
Xcode 15.0
Build version 15A240d
I'm experiencing a peculiar issue with SwiftUI's TextField. Whenever I long-press on the TextField, the console outputs an error about passing an invalid numeric value (NaN, or not-a-number) to the CoreGraphics API. This issue persists even in a new Xcode project with minimal code.
Code Snippet:
import SwiftUI
struct ContentView: View {
@State private var text: String = ""
var body: some View {
TextField("Placeholder", text: $text)
}
}
Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem.
Steps to Reproduce:
Create a new SwiftUI project in Xcode.
Add a TextField to the ContentView.
Run the app on a device or simulator.
Long-press inside the TextField.
What I've Tried:
Updating to the latest version of Xcode and iOS.
Using UIViewRepresentable to wrap a UIKit UITextField.
Creating a new Xcode project to isolate the issue.
None of these steps have resolved the issue.
Questions:
Has anyone else encountered this problem?
Are there any known workarounds for this issue?
Is this a known bug, and if so, has it been addressed in any updates?