Hi,
I'm trying to create a custom bottom toolbar for my app and want to use same fade-blur effect as iOS uses under navigation and tab bars. Having trouble doing that.
Here is what I tried:
Screenshot 1: putting my custom view in a toolbar/ToolBarItem(placement: .bottomBar). This works only in NavigationStack, and it adds a glass pane that I do not want (I want to put a custom component there that already has correct glass pane)
Screenshot 2: using safeAreaBar or safeAreaInset in any combination with NavigationStack and/or .scrollEdgeEffectStyle(.soft, for: .bottom). Shows my component correctly, but does not use fade-blur.
Can you please help me to find out the correct way of doing that? Thanks!
^ Screenshot 1
^ Screenshot 2
Test code:
struct ContentView2: View {
var body: some View {
NavigationStack {
ScrollView(.vertical) {
VStack {
Color.red.frame(height: 500)
Color.green.frame(height: 500)
}
}
.ignoresSafeArea()
.toolbar() {
ToolbarItem(placement: .bottomBar) {
HStack {
Text("bottom")
Spacer()
Text("text content")
}
.bold().padding()
.glassEffect().padding(.horizontal)
}
}
}
}
}
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hi all,
I’m trying to implement a collapsible section in a List on watchOS (watchOS 10+). The goal is to have a disclosure chevron that toggles a section open/closed with animation, similar to DisclosureGroup on iOS.
Unfortunately, DisclosureGroup is not available on watchOS. 😢
On iOS, this works as expected using this Section init:
Section("My Header", isExpanded: $isExpanded) {
// content
}
That gives me a tappable header with a disclosure indicator and the animation built in, as expected.
But on watchOS, this same init displays the header, but it’s not tappable, and no disclosure triangle appears.
I’ve found that to get it working on watchOS, I need to use the other initializer:
Section(isExpanded: $isExpanded) {
// content
} header: {
Button(action: { isExpanded.toggle() }) {
HStack {
Title("My Header")
Spacer()
Image(systemName: isExpanded ? "chevron.down" : "chevron.right")
}
}
That works, but feels like a workaround. Is this the intended approach for watchOS, or am I missing a more native way to do this?
Any best practices or alternative recommendations appreciated.
Thanks!
I just wanted to post this here because since we started using SwiftUI, SwiftUI Previews have been painful to use, and then became basically unusable. And then after many hours of investigating, I seem to have found a solution and I wanted to share it somewhere useful.
The symptoms were that when clicking in the circle to enable preview (from the preview paused state) for any view, the progress spinner would sit there for... sometimes forever, but sometimes for 20 minutes before a preview would show.
It wasn't just slow, it was entirely unusable. Most of the complaints I've seen from other developers are about it being slow and unresponsive, but for us, it would just spin infinitely (rarely it would stop and then the preview would display). None of the 'fixes' here or on StackOverflow, etc helped.
So after a year of just mostly not using SwiftUI Previews, I found something interesting - under the build tab, the app was continually being rebuilt when the preview canvas with the spinner was being shown.
What?
OK, so it turns out we have a Run Script build step that generates output (a Credits.plist for open source libraries). It did not declare output files, which XCode had long complained about, but in general was not an issue... except apparently for SwiftUI Previews.
Checking the "For install builds. only" checkbox for this build step made the problem go away. That was it. SwiftUI Previews build and run fairly well now.
I have not tried fixing the complaint about the build script outputs - that might have also fixed the problem, but "For install builds only" definitely fixed our issue.
I am trying out the new AttributedString binding with SwiftUI’s TextEditor in iOS26. I need to save this to a Core Data database. Core Data has no AttributedString type, so I set the type of the field to “Transformable”, give it a custom class of NSAttributedString, and set the transformer to NSSecureUnarchiveFromData
When I try to save, I first convert the Swift AttributedString to NSAttributedString, and then save the context. Unfortunately I get this error when saving the context, and the save isn't persisted:
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x600003721140> , <shared NSSecureUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
Here's the code that tries to save the attributed string:
struct AttributedDetailView: View {
@ObservedObject var item: Item
@State private var notesText = AttributedString()
var body: some View {
VStack {
TextEditor(text: $notesText)
.padding()
.onChange(of: notesText) {
item.attributedString = NSAttributedString(notesText)
}
}
.onAppear {
if let nsattributed = item.attributedString {
notesText = AttributedString(nsattributed)
} else {
notesText = ""
}
}
.task {
item.attributedString = NSAttributedString(notesText)
do {
try item.managedObjectContext?.save()
} catch {
print("core data save error = \(error)")
}
}
}
}
When using the writingToolsBehavior API on a TextField and the app compiled with the iOS 26 SDK is run on an iOS 18 device, the app crashes with a symbol not found error.
It only crashes on the release build configuration and not on debug.
dyld[5274]: Symbol not found: _$s7SwiftUI17EnvironmentValuesV21_writingToolsBehaviorAA07WritingfG0VSgvg Referenced from: <1306655E-6DF7-3B2A-94A3-7202149E82F3> /private/var/containers/Bundle/Application/88E47904-4884-4279-9E96-0EC366970389/WritingToolsTest.app/WritingToolsTest Expected in: <165D3305-401E-37C2-8387-C1BFB54CFFDE> /System/Library/Frameworks/SwiftUI.framework/SwiftUI
Feedback ID: FB17980516
Although I can't see anything in Apple's documentation to this effect, I'm coming to believe that ReferenceFileDocument is incompatible with @Observable.
But hopefully I've just missed something!
I have an app in which the data model is @Observable, and views see it through
@Environment(dataModel.self) private var dataModel
Since there are a large number of views, only some of which may need to be redrawn at a given time, Apple's documentation leads me to believe that @Observable may be smarter about only redrawing views that actually need redrawing than @Published and @ObservedObject.
I originally wrote the app without document persistence, and injected the data model into the environment like this:
@main
struct MyApp: App {
@State private var dataModel = DataModel()
var body: some Scene {
WindowGroup {
myDocumentView()
.environment(dataModel)
}
}
}
I’ve been trying to make the app document based. Although I started using SwiftData, it has trouble with Codable (you need to explicitly code each element), and a long thread in the Developer forum suggests that SwiftData does not support the Undo manager - and in any event, simple JSON serialization is all that this app requires - not a whole embedded SQLLite database.
At first, it seems to be easy to switch to a DocumentGroup:
@main
struct MyApp: App {
var body: some Scene {
DocumentGroup(newDocument: {DataModel() } ) { file in
myDocumentView()
.environment(file.document) }
}
}
Since I've written everything using @Observable, I thought that I'd make my data model conform to ReferenceFileDocument like this:
import SwiftUI
import SwiftData
import UniformTypeIdentifiers
@Observable class DataModel: Identifiable, Codable, @unchecked Sendable, ReferenceFileDocument {
// Mark: ReferenceFileDocument protocol
static var readableContentTypes: [UTType] {
[.myuttype]
}
required init(configuration: ReadConfiguration) throws {
if let data = configuration.file.regularFileContents {
let decodedModel = try MyModel(json: data)
if decodedModel != nil {
self = decodedModel!
} else {
print("Unable to decode the document.")
}
} else {
throw CocoaError(.fileReadCorruptFile)
}
}
func snapshot(contentType: UTType) throws -> Data {
try self.json()
}
func fileWrapper(snapshot: Data,
configuration: WriteConfiguration) throws -> FileWrapper {
FileWrapper(regularFileWithContents: snapshot)
}
var nodes = [Node]() // this is the actual data model
init() {
newDocument()
}
... etc.
I've also tried a similar approach in which the ReferenceFileDocument is a separate module that serializes an instance of the data model.
The problem I'm currently experiencing is that I can't figure out how to:
a) inject the newly created, or newly deserialized data model into the environment so that views can take advantage of it's @Observable properties, or
b) how to cause changes in the @Observable data model to trigger serialization (actually I can observe them triggering serialization, but what's being serialized is an empty instance of the data model).
I make data model changes through a call to the Undo manager:
// MARK: - Undo
func undoablyPerform(_ actionName: String, with undoManager: UndoManager? = nil, doit: () -> Void) {
let oldNodes = self.nodes
doit()
undoManager?.registerUndo(withTarget: self) { myself in
self.undoablyPerform(actionName, with: undoManager) {
self.nodes = oldNodes
}
}
undoManager?.setActionName(actionName)
}
The views looks like this:
import SwiftUI
import CoreGraphics
struct myDocumentView: View {
@Environment(DataModel.self) private var dataModel
@Environment(\.undoManager) var undoManager
... etc.
Some things work - if I prepopulate the model, it serializes correctly, and gets written to a file.
Unfortunately, in the view hierarchy, myModel is always empty.
Have I done something wrong? Do I need to abandon @Observable?
I've tried conforming the model to ObservedObject, adding @Published, and injecting it as an @ObservedObject - and viewing as @EnvironmentObject var dataModel: DataModel
But it's still not injected correctly into the View hierarchy.
Edit - I may have identified the problem - will update this question when confirmed.
According to this video, in iOS 26 there should be an containerConcentric configuration that can be passed to the corner parameter of a rectangle.
But when trying this on my own, it looks like there is no cornerparameter and also no containerConcentric configuration. Also, I can't find this in the SwiftUI documentation.
.glassProminent not working, but .glass works for .buttonStyle()
as used here
https://youtu.be/3MugGCtm26A?si=dvo2FeE88OnNIwI9&t=938
/Users/brianruiz/repos/taskss/taskss/Views/Components/EmptyStateView.swift:125:39 Reference to member 'glassProminent' cannot be resolved without a contextual type
if #available(iOS 26.0, *) {
Button(action: {
HapticManager.shared.selection()
action()
}) {
Text(buttonLabel ?? "Action")
.frame(maxWidth: .infinity)
}
.padding(.horizontal, 24)
.buttonStyle(.glassProminent)
.buttonBorderShape(.capsule)
.controlSize(.large)
.tint(.primary)
.offset(y: buttonOffset)
.opacity(buttonOpacity)
.scaleEffect(isPressed ? 0.95 : 1.0)
.animation(.bouncy(), value: isPressed)
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: 50, pressing: { pressing in
isPressed = pressing
}, perform: {})
.onAppear {
guard animate else { return }
withAnimation(.bouncy().delay(0.6)) {
buttonOffset = 0
buttonOpacity = 1
}
}
} else {
// Fallback on earlier versions
}
Hello dear community!
I'm a new to SwiftUI and going through the official Develop in Swift Tutorials.
I've just finished the chapter Customize views with properties where it was show that it's possible to declare computed properties like this:
var iconName: String {
if isRainy {
return "cloud.rain.fill"
} else {
return "sun.max.fill"
}
}
And then use it as follows:
var body: some View {
VStack {
Text(day)
Image(systemName: iconName)
}
}
This is really cool and I liked it and I decided that I want to do the similar thing for the project from the previous chapter (ChatBubble), so I decided to declare the following property in my custom ChatBubble view:
struct ChatBubble: View {
let text: String
let color: Color
let isEllipsis: Bool
var shape: Shape {
if isEllipsis {
Ellipse()
} else {
RoundedRectangle(cornerRadius: 8)
}
}
var body: some View {
Text(text)
.padding()
.background(color, in: shape)
}
}
But first, I got a warning where I declare shape: Shape that it must be written any Shape like this:
var shape: any Shape { …
But in both cases I got a error in the body:
'buildExpression' is unavailable: this expression does not conform to 'View'
What does it mean? Why I can't use the computed property with shape like the computed property for String?
Thank you very much in advance.
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hi, has anyone noticed that when using SwiftUI the MapKit Marker created with
Marker(item: MKMapItem)
.tint(.red) //solid flat color
ignores the default marker styling (the nice gradient and shadow) and shows only a flat solid fill?
The shadow/gradient appears correctly with some colors like .blue or .orange, but disappears with others such as .red, .purple, etc.
What’s odd is that this happens only with the init(item: MKMapItem) initializer. A marker created with, for example, following init works just fine.
Marker("hello", coordinate: CLLocationCoordinate2D)
.tint(.red) //nice shadow/gradient
Is this a bug, or does a marker backed by an MKMapItem support only a limited color range? (If so, exposing .tint there seems inconsistent—either all colors should work or none.)
Has anyone else run into this problem?
.orange with shadow/gradient:
.red solid without shadow/gradient:
I have an iOS Widget that also can load on the Mac when the Use iPhone Widgets setting is turned on on the Mac in Desktop & Dock.
I want to use a different url scheme to open video clips from the widget if it is being clicked on iOS or the Mac.
I tried using ProcessInfo.processInfo.isiOSAppOnMac but it always thinks it is on iOS.
I also tried looking for the user document path to see if it was /var/mobile/ or /Users/. but it always thinks it is /var/mobile.
I assume this is as it is not really a catalyst app but a WidgetKit extension from the phone.
Is there anyway I can figure out when the widget is running on the mac?
Thanks!
There is an issue with SwiftUI where it is incorrectly instantiating the navigation destination view with a new identity for a navigation stack that is being removed.
Here is a minimal reproducible example:
struct ContentView: View {
@State private var signedIn = false
var body: some View {
if signedIn {
navigationStack(isSignedIn: true)
} else {
navigationStack(isSignedIn: false)
}
}
private func navigationStack(isSignedIn: Bool) -> some View {
NavigationStack(path: .constant(NavigationPath([1]))) {
EmptyView()
.navigationDestination(for: Int.self) { _ in
VStack {
Text(isSignedIn ? "Signed In" : "Signed Out")
.foregroundStyle(Color.red)
Button(isSignedIn ? "Sign Out" : "Sign In") {
signedIn = !isSignedIn
}
}
.onFirstAppear {
print(isSignedIn ? "signed in" : "signed out")
}
}
}
}
}
struct OnFirstAppearView: ViewModifier {
@State private var hasAppeared = false
var onAppear: () -> Void
func body(content: Content) -> some View {
content
.onAppear {
if hasAppeared { return }
hasAppeared = true
onAppear()
}
}
}
extension View {
func onFirstAppear(_ onAppear: @escaping () -> Void) -> some View {
ModifiedContent(content: self, modifier: OnFirstAppearView(onAppear: onAppear))
}
}
When you launch the app it will print "signed out", but when you tap to Sign In it will print "signed out" and "signed in". This shows that onAppear is incorrectly being called for a view that is disappearing and worse yet, it is with a new identity.
The onFirstAppear modifier was created to help with detecting the identity change of the view.
Tested on Xcode 16.4, on simulator using iOS 18.5 and also on physical device using iOS 18.5.
Link to Feedback sent on Feedback Assistant: https://feedbackassistant.apple.com/feedback/18336684
I'm trying to create two widgets, widget A and B.
Currently A and B are very similar so they share the same Intent and Intent Timeline Provider.
I use the Intent Configuration interface to set a parameter, in this example lets say its the background tint.
On one of the widgets, widget A, I want to also set another String enum parameter (for a timescale), but I don't want this option to be there for widget B as it's not relevant.
I'm aware of some of the options for configuring the ParameterSummary, but none that let me pass in or inject the "kind" string (or widget ID) of the widget that's being modified.
I'll try to provide some code for examples.
My Widget Definition (targeting >= iOS 17)
struct WidgetA: Widget {
// I'd like to access this parameter within the intent
let kind: String = "WidgetA"
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in
WidgetView(data: entry)
}
.configurationDisplayName("Widget A")
.description("A widget.")
.supportedFamilies([.systemMedium, .systemLarge])
}
}
struct WidgetB: Widget {
let kind: String = "WidgetB"
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: kind, intent: WidgetIntent.self, provider: IntentTimelineProvider()) { entry in
WidgetView(data: entry)
}
.configurationDisplayName("Widget B")
.description("B widget.")
.supportedFamilies([.systemMedium, .systemLarge])
}
}
struct IntentTimelineProvider: AppIntentTimelineProvider {
typealias Entry = WidgetIntentTimelineEntry
typealias Intent = WidgetIntent
........
}
struct WidgetIntent: AppIntent, WidgetConfigurationIntent {
// This intent allows configuration of the widget background
// This intent also allows for the widget to display interactive buttons for changing the Trend Type
static var title: LocalizedStringResource = "Widget Configuration"
static var description = IntentDescription("Description.")
static var isDiscoverable: Bool { return false}
init() {}
init(trend:String) {
self.trend = trend
}
// Used for implementing interactive Widget
func perform() async throws -> some IntentResult {
print("WidgetIntent perform \(trend)")
#if os(iOS)
WidgetState.setState(type: trend)
#endif
return .result()
}
@Parameter(title: "Trend Type", default: "Trend")
var trend:String
// I only want to show this parameter for Widget A and not Widget B
@Parameter(title: "Trend Timescale", default: .week)
var timescale: TimescaleTypeAppEnum?
@Parameter(title: "Background Tint", default: BackgroundTintTypeAppEnum.none)
var backgroundTint: BackgroundTintTypeAppEnum?
static var parameterSummary: some ParameterSummary {
// Summary("Test Info") {
// \.$timescale
// \.$backgroundTint
// }
// An example of a configurable widget parameter summary, but not based of kind/ID string
When(\.$backgroundTint, .equalTo, BackgroundTintTypeAppEnum.none) {
Summary("Test Info") {
\.$timescale
\.$backgroundTint
}
} otherwise : {
Summary("Test Info") {
\.$backgroundTint
}
}
}
}
enum TimescaleTypeAppEnum: String, AppEnum {
case week
case fortnight
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Trend Timescale")
static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [
.week: "Past Week",
.fortnight: "Past Fortnight"
]
}
enum BackgroundTintTypeAppEnum: String, AppEnum {
case blue
case none
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Background Tint")
static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [
.none: "None (Default)",
.blue: "Blue"
]
}
I know I could achieve what I'm after by having a separate Intent and separate IntentTimelineProvider for each widget. But this all seems unnessecary for just a simple optional parameter based on what widget its configuring.... unless I'm missing the point about Intents, Widgets or something!
I've done a fair bit of other searching but can't find an answer to this overall scenario.
Many thanks for any help.
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?
I'm building a SwiftUI app using SwiftData. In my app I have a Customer model with an optional codable structure Contact. Below is a simplified version of my model:
@Model class Customer {
var name: String = ""
var contact: Contact?
init(name: String, contact: Contact? = nil) {
self.name = name
self.contact = contact
}
struct Contact: Codable, Equatable {
var phone: String
var email: String
var allowSMS: Bool
}
}
I'm trying to query all the Customers that have a contact with @Query. For example:
@Query(filter: #Predicate<Customer> { customer in
customer.contact != nil
}) var customers: [Customer]
However no matter how I set the predicate I always get an error:
BugDemo crashed due to an uncaught exception NSInvalidArgumentException. Reason: keypath contact not found in entity Customer.
How can I fix this so that I'm able to filter by contact not nil in my Model?
I have a toolbar in SwiftUI where two of the buttons are Menus. When I tap on the button the menu animates out into the correct position and then it flys to the top of the screen.
When I tap on the menu button I get this.
Then within 1 second the menu flys to the top of the screen and looks like this.
My toolbar code looks like this.
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Menu {
Button { markMyLocation() } label: { Label("Mark My Location", systemImage: "location") }
... (MORE BUTTONS)
} label: {
Image(systemName: "plus")
}
.menuOrder(.fixed)
Spacer()
Menu {
Button { shareWithWebLink() } label: { Label("Share Map", systemImage: "square.and.arrow.up") }
... (MORE BUTTONS)
} label: {
Image(systemName: "square.and.arrow.up")
}
.menuOrder(.fixed)
Button {
presentHikeView()
} label: {
Image(systemName: "figure.hiking")
}
Button {
presentMapsView()
} label: {
Image(systemName: "map")
}
Spacer()
Button {
search()
} label: {
Image(systemName: "magnifyingglass")
}
}
}
Suppose you have a view:
struct Library: View {
@State var books = []
var body: some View {
VStack {
...
Button("add Book") {
....
}
}
}
Such that Library view holds a Book array that gets populated internally via a button that opens a modal – or something alike.
How can I use #Peviews to preview Library with a pre-populated collection? Suppose I have a static books – dummy – array in my code just for that; still, what's the best way to use that to preview the Library view?
I’m testing .glassEffect() in a widget on the latest iOS 26 beta, but it seems to have no effect at all — the blur isn’t visible, and in some cases even white text disappears completely.
Is this the expected behavior for widgets (i.e. glass effect is not supported), or could this be a bug in the beta?
Would appreciate any clarification or pointers. Thanks!
An WWDC 25 a neutral value was demonstrated that allows the Slider to be 'coloured in' from the point of the neutral value to the current thumb position. Trying to use this in Dev release 1 I get errors saying no such modifier.
Was this functionality released in Dev Release 1 or am I using it incorrectly?
.navigationTitle disappears when using .toolbar and List inside NavigationStack (iOS 26 beta)
Summary
In iOS 26 beta, using .navigationTitle() inside a NavigationStack fails to render the title when combined with a .toolbar and a List. The title initially appears as expected after launch, but disappears after a second state transition triggered by a button press. This regression does not occur in iOS 18.
Steps to Reproduce
Use the SwiftUI code sample below (see viewmodel and Reload button for state transitions).
Run the app on an iOS 26 simulator (e.g., iPhone 16).
On launch, the view starts in .loading state (shows a ProgressView).
After 1 second, it transitions to .loaded and displays the title correctly.
Tap the Reload button — this sets the state back to .loading, then switches it to .loaded again after 1 second.
❌ After this second transition to .loaded, the navigation title disappears and does not return.
Actual Behavior
The navigation title displays correctly after the initial launch transition from .loading → .loaded.
However, after tapping the “Reload” button and transitioning .loading → .loaded a second time, the title no longer appears.
This suggests a SwiftUI rendering/layout invalidation issue during state-driven view diffing involving .toolbar and List.
Expected Behavior
The navigation title “Loaded Data” should appear and remain visible every time the view is in .loaded state.
✅ GitHub Gist including Screen Recording
👉 View Gist with full details
Sample Code
import SwiftUI
struct ContentView: View {
private let vm = viewmodel()
var body: some View {
NavigationStack {
VStack {
switch vm.state {
case .loading:
ProgressView("Loading...")
case .loaded:
List {
ItemList()
}
Button("Reload") {
vm.state = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
vm.state = .loaded
}
}
.navigationTitle("Loaded Data")
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Text("hello")
} label: {
Image(systemName: "gearshape.fill")
}
}
}
}
}
}
struct ItemList: View {
var body: some View {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
@MainActor
@Observable
class viewmodel {
enum State {
case loading
case loaded
}
var state: State = .loading
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.state = .loaded
}
}
}