Scenario
A SwiftUI view has an overlay with alignment: .top, the content uses .alignmentGuide(.top) {} to adjust the placement.
Issue
When the content of the overlay is in an if-block, the alignment guide is not adjusted.
Example code
The example shows 2 views.
Not working example, where the content is an if-block.
Working example, where the content is not in an if-block
Screenshot: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/blob/main/screenshot.png
Tested on
- Xcode Version 16.0 RC (16A242) on iOS 18.0
Code
// Not working
.overlay(alignment: .top) {
if true { // This line causes .alignmentGuide() to fail
Text("Test")
.alignmentGuide(.top, computeValue: { dimension in
dimension[.bottom]
})
}
}
// Working
.overlay(alignment: .top) {
Text("Test")
.alignmentGuide(.top, computeValue: { dimension in
dimension[.bottom]
})
}
Also created a Feedback: FB15248296
Example Project is here: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/tree/main
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
In one of my applications I use several List views with Sections. After upgrading to Sequoia I faced the issue, that after selecting an item, the List suddenly scrolls to a different position. Sometimes the selection even gets out of the view, but in every case a double click just went to the wrong item.
At one list I found out, that the issue could be solved after changing the data source. I used a computed property, what seems to be a stupid idea. After changing this it now works.
Unfortunately there is another List, where this didn't bring the solution. And unfortunately, I cannot reproduce the issue in a code example. One guess of mine is, that it could be related to the fact, that the rows have different heights (because in some are two lines of text and in some are three). And it seems to happen only in very long lists.
It worked perfectly in Sonoma.
Does anyone face the same issue?
I am using SwiftUI NavigationView to navigate. I cant find how can i disable swipe from the leftmost part of the screen for navigation bar. In tried this way , but it doesn't work for me:
struct DisableSwipeBackGesture: ViewModifier {
func body(content: Content) -> some View {
content
.background(DisableBackSwipeGestureView())
}
}
struct DisableBackSwipeGestureView: UIViewControllerRepresentable {
typealias UIViewControllerType = DisableSwipeBackViewController
func makeUIViewController(context: Context) -> DisableSwipeBackViewController {
DisableSwipeBackViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
}
final class DisableSwipeBackViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController {
if let interactivePopGestureRecognizer = navigationController.interactivePopGestureRecognizer {
interactivePopGestureRecognizer.delegate = self
interactivePopGestureRecognizer.isEnabled = false
}
}
}
}
extension DisableSwipeBackViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
extension View {
func disableSwipeBackGesture() -> some View {
self.modifier(DisableSwipeBackGesture())
}
}
Is there a way to disable this feature?
I am using SwiftUI NavigationView to navigate. I cant find how can i disable swipe from the leftmost part of the screen for navigation bar. Im tried this way , but it doesn't work for me:
struct DisableBackSwipeGestureView: UIViewControllerRepresentable {
typealias UIViewControllerType = UINavigationController
func makeUIViewController(context: Context) -> UINavigationController {
DisableSwipeBackViewController().navigationController ?? UINavigationController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator: NSObject, UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
}
final class DisableSwipeBackViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController {
if let interactivePopGestureRecognizer = navigationController.interactivePopGestureRecognizer {
interactivePopGestureRecognizer.delegate = self
interactivePopGestureRecognizer.isEnabled = false
}
}
}
}
extension DisableSwipeBackViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
extension View {
func disableSwipeBackGesture() -> some View {
self.modifier(DisableSwipeBackGesture())
}
}
Is there a way to disable this feature in swiftui?
Hello,
I'm encountering an issue with toolbarColorScheme in iOS 18. In a simple example, toolbarColorScheme works fine when triggered in onAppear. However, after navigating to a different view (e.g., following a link, such as link 234), and then returning, the toolbarColorScheme seems to be ignored.
Could anyone help me resolve this issue?
struct TestNavigationBarColor: View {
var body: some View {
NavigationStack {
List {
Text("123")
NavigationLink(value: 1) {
Text("234")
}
}
.toolbarColorScheme(.dark, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Title1")
.navigationDestination(for: Int.self) {_ in
List {
Text("Nested screen")
}
.navigationTitle("Title2")
}
}
}
}
Hello,
In our current project we are using NavigationSplitView in the following way:
var body: some View {
NavigationSplitView(
sidebar: {
ListView()
},
detail: {
DetailsView()
}
)
.navigationBarHidden(true)
.navigationSplitViewStyle(.automatic)
.introspectSplitViewController { splitViewController in
splitViewController.preferredDisplayMode = .oneOverSecondary
splitViewController.maximumPrimaryColumnWidth = 500
splitViewController.preferredPrimaryColumnWidthFraction = 1
}
}
}
And in iPhone the DetailsView() is pushed using the navigationDestination modifier:
.navigationDestination(isPresented: $isShowingDetailsScreen) {
DetailsView()
.navigationBarHidden(true)
}
where isShowingDetailsScreen is updated through a button click:
Button() {
self.isShowingDetailsScreen = true
}
This works perfectly in iOS versions prior to 18. However starting from this version the button click no longer presents the view desired. Below is what happening in the latest iOS versions:
iOS 18: This mechanism is broken altogether, it does not work at all.
iOS 18.1: This mechanism is partially broken, when clicking on the button the DetailsView is not presented, however since the page also contains other reactive views, any time one of them is changed through a state change after clicking on that button, the DetailsView is presented.
Does anyone faced an issue like this with the new iOS and if so is there any workaround for now? Because this seems to be a bug caused by changes in SwiftUI and shouldn't persist for long.
I was creating a sample SwiftData project using two models and a one to one relationship. Before upgrading to xcode 16, everything was fine and compiled correctly. After upgrading to xcode 16, I get fatal errors: "This model instance was destroyed by calling ModelContext.reset and is no longer usable" or
I have a button on a live activity, and I want to end the live activity and update the Control Center widgets and home screen widgets after clicking this button. So, in the perform() method of the button's LiveActivityIntent, I called the methods ControlCenter.shared.reloadAllControls() and WidgetCenter.shared.reloadAllTimelines(), but they didn't work. Home screen widgets and Control Center widgets do not refresh, resulting in a poor user experience.
When using the new .navigationTransition feature, when using .searchable at the same time result in the search bar disappearing when dismissing the view you've trasition to, has anyone else experienced this or found any workarounds? Here is an example that make the issue always occur.
@State private var searchText: String = ""
@Namespace private var namespace
let things: [String] = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirdteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
var body: some View {
NavigationStack {
ScrollView {
LazyVStack(spacing: 20) {
ForEach(things, id: \.self) { thing in
NavigationLink(){
SwiftUIView(thing: thing, name: namespace)
}label: {
Text(thing)
}
.matchedTransitionSource(id: thing, in: namespace)
}
}
}
.searchable(text: $searchText)
.navigationTitle("My List")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct SwiftUIView: View {
var thing: String
var name: Namespace.ID
var body: some View {
Text(thing)
.navigationTransition(.zoom(sourceID: thing, in: name))
}
}
After running the code you end up with this:
And after clicking an element and dismissing it your get this:
Hi! I have a stateful object that should be created in my app entry point and delivered through my component graph:
@main
@MainActor struct MyApp : App {
@State private var myAppState = MyAppState()
var body: some Scene {
...
}
}
The MyApp is a struct value type… in a SwiftUI world that seems to imply it could "go away" and be recreated as the system sees appropriate. We see this with view components all the time (hence the State wrapper to help preserve our instance across the lifecycle of our identity)… but I'm wondering if this State wrapper is even necessary with an App entry point. Could this struct ever go away? Would there be any legit reasons that this struct should go away and recreate over one SwiftUI app lifecycle (other than terminating the app and starting a whole new process of course)?
And what lifecycle is the SwiftUI.State tied to in this example? In a view component our SwiftUI.State is tied to our component identity. In this example… we are tied to app component identity? Is there ever going to be multiple legit app component identities live in the same process?
I'm thinking I could just go ahead and keep using State as a best practice… but is this just overkill or is there a real best practice lurking under here? Any more ideas about that? Thanks!
I'm facing an issue with SwiftUI's NavigationStack when using the searchable modifier. Everything works as expected when navigating between views, but if I use the search bar to filter a list and then tap on a filtered result, I can navigate to the next view. However, in the subsequent view, my "Set and Return to Root" button, which is supposed to call popToRoot(), does not work. Here's the setup:
Structure: RootView: Contains a list with items 1-7. ActivityView: Contains a list of activities that can be filtered with the searchable modifier. SettingView: Contains a button labeled "Set and Return to Root" that calls popToRoot() to navigate back to the root view.
RootView
struct RootView: View {
@EnvironmentObject var navManager: NavigationStateManager
var body: some View {
NavigationStack(path: $navManager.selectionPath) {
List(1...7, id: \.self) { item in
Button("Element \(item)") {
// Navigate to ActivityView with an example string
navManager.selectionPath.append(NavigationTarget.activity)
}
}
.navigationTitle("Root View")
.navigationDestination(for: NavigationTarget.self) { destination in
switch destination {
case .activity:
ActivityView()
case .settings:
SettingsView()
}
}
}
}
}
ActivityView
struct ActivityView: View {
@EnvironmentObject var navManager: NavigationStateManager
let activities = ["Running", "Swimming", "Cycling", "Hiking", "Yoga", "Weightlifting", "Boxing"]
@State private var searchText = ""
var filteredActivities: [String] {
if searchText.isEmpty {
return activities
} else {
return activities.filter { $0.localizedCaseInsensitiveContains(searchText) }
}
}
var body: some View {
List {
ForEach(filteredActivities, id: \.self) { activity in
NavigationLink(
destination: SettingsView(), // Navigiere zur SettingsView
label: {
HStack {
Text(activity)
.padding()
Spacer()
}
}
)
}
}
.navigationTitle("Choose Activity")
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search Activities")
}
}
SettingView
struct SettingsView: View {
@EnvironmentObject var navManager: NavigationStateManager
var body: some View {
VStack {
Text("Settings")
.font(.largeTitle)
.padding()
Button("Set and Return to Root") {
// Pop to the root view when the button is pressed
navManager.popToRoot()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.navigationTitle("Settings")
}
}
NavigationStateManager
// Define enum globally at the top
enum NavigationTarget {
case activity
case settings
}
class NavigationStateManager: ObservableObject {
@Published var selectionPath = NavigationPath()
func popToRoot() {
selectionPath = NavigationPath()
}
func popView() {
selectionPath.removeLast()
}
}
Problem
When I search in the ActivityView and tap on a filtered result, I successfully navigate to the SettingView. However, in this view, pressing the "Set and Return to Root" button does not trigger the navigation back to RootView, even though popToRoot() is being called.
This issue only occurs when using the search bar and filtering results. If I navigate without using the search bar, the button works as expected.
Question
Why is the popToRoot() function failing after a search operation, and how can I ensure that I can return to the root view after filtering the list?
Any insights or suggestions would be greatly appreciated!
When there are more than 5 tabs in TabView, the tabs from the 5th and on get put in the "More" tab as a list.
But when each tab has its own NavigationStack, the tabs in "More" would have double navigation bars. The expected behavior is there should be only one navigation bar, and NavigationStack for tabs in "More" should be collapsed with navigation for the "More" tab itself.
Minimal reproducible case:
Run the code below as an app
Navigate to "More" tab
Navigate to "Tab 5" or "Tab 6"
You can see there are two navigation bars stacked on top of each other
struct Item: Identifiable {
let name: String
var id: String { name }
}
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
let items = [
Item(name: "Tab 1"),
Item(name: "Tab 2"),
Item(name: "Tab 3"),
Item(name: "Tab 4"),
Item(name: "Tab 5"),
Item(name: "Tab 6"),
]
var body: some View {
TabView {
ForEach(items) { item in
NavigationStack {
List {
Text(item.name)
}.navigationTitle(item.name)
}.tabItem {
Label(item.name, systemImage: "person")
}
}
}
}
}
Before iOS 18, I can get around this issue by making my own "More" tab. But now with the expectation that user can re-arrange the tabs and all tabs would show up in the sidebar, the "make my own more tab" approach no longer work very well.
Since iOS 18 I have noticed a strange issue which happens to the TabView in SwiftUI when you switch from Dark to Light mode.
With this code:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
List {
ForEach(0..<100, id: \.self) { index in
Text("Row: \(index)")
}
}.tabItem {
Image(systemName: "house")
Text("Home")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The TabBar does not switch colours when going from dark to light, it work correctly when going from light to dark.
Here is a video of iOS 18 with the issue:
Here is a video of iOS 17.5 with same code and no issue:
On real device it looks (better), the color does update but with delay.
I have submitted a feedback bug report, but in the meanwhile has anyone been back to resolve this?
Some users have reported that the app's widgets have disappeared after we updated to the latest Xcode for iOS 18 development. They are unable to find our app in the widget search, which prevents them from re-adding the widget. We were unable to reproduce this issue on our own devices during testing. Could this be a system bug? Have others encountered a similar issue?
Hi, after update to Swift6 and iOS 18 my code is not working anymore. This is code from documentation with onTapGesture (instead of primaryAction) and it's not working. OnTapGesture is not starting when user click.
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} onTapGesture: {
print(" Print on tap gesture")
}
primaryAction isn't working either. I mean, primary action works but menu is not showing inside buttons.
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
} primaryAction: {
print("")
}
I also tried to overlay the menu, no effect.
ZStack {
Menu {
Button(action: {}) {
Label("Add to Reading List", systemImage: "eyeglasses")
}
Button(action: {}) {
Label("Add Bookmarks for All Tabs", systemImage: "book")
}
Button(action: {}) {
Label("Show All Bookmarks", systemImage: "books.vertical")
}
} label: {
Label("Add Bookmark", systemImage: "book")
}
Color.clear
.contentShape(Rectangle())
.onTapGesture {
print("Tap gesture recognized")
}
}
Please help. How to start action when user is clicking on menu?
What is current best-practice for supporting an external display in a SwiftUI iOS app in 2024?
(I'm only interested in iOS 17 and/or iOS 18)
The only docs I found require abandoning the SwiftUI App structure and "switching back to a full UIKit App Delegate".
Is this still the only option?
Are there any complete examples on how to accomplish this?
Also is testing external displays with Simulator at all reliable? All experiences I read about say that it never works.
Thanks in advance.
There seems to be a performance issue when scrolling using the track pad with SwiftUI scroll views in macOS 15. This issue is NOT present in macOS 14.
When using the track pad the scrolling is not smooth, but "stutters". However scrolling using the scroll bars is very smooth. The "stuttering" is worse if the SwiftUI ScrollView is in the detail view of a NavigationSplitView.
The problem is not noticeable in scroll views with a small number views, but when the more views inside the scroll view, the more prominent the problem becomes.
I have a simple example app that illustrates the problem here (the example app is a simplification of my app Yammixer): https://github.com/danwaltin/SwiftUIScrollViewPerformance
When running this example app on macOS 14 (Sonoma) on an Intel i7 Mac book pro from 2019 the scrolling is "buttery smooth".
But on macOS 15 (Sequoia) on my Apple Silicon M1 Mac book pro the issue is very obvious.
When using Instruments I see that on macOS 15 "flame graph" shows that 85% of the execution time is in a "_hitTestForEvent" method. If the test app does not use NavigationSplitView about 70% of execution time is in the _hitTestForEvent method.
I am trying out the new TextField selection ability on macOS but it crashes in various different ways with extremely large stack traces. Looks like it is getting into re-entrant function calls.
A similar problem is described on the SwiftUI forums with no responses yet.
Here is my simple example
struct ContentView: View {
@State private var text: String = ""
@State private var selection: TextSelection?
var body: some View {
TextField("Message", text: $text, selection: $selection)
.padding()
}
}
Setting text to a value like "Hallo World" causes an instant crash as soon as you start typing in the TextField.
Setting text empty (as in example above) lets you edit the text but as it crashes as soon as you commit it (press enter).
Any workarounds or fixes?
Hello,
After a fair amount of time of trial and error, I have seemingly discovered a bug in iOS 18 with .toolBar with placement:. keyboard)
Feedback: FB15205988
Xcode Version: 16.0 (16A242d)
iOS Version: 18.0 (22A3351)
I'd appreciate help to see if I'm missing something here.
What's happening?
When using .toolBar with (placement: .keyboard), within a NavigationSplitView detail:, encompassed in a TabView, the .toolBar view does not appear above the keyboard.
Within the code, the usage of .toolBar is within ShakeDetail.swift.
When commenting out the TabView within ShakeTabView.swift, or running on iOS 17.5, the .toolbar appears.
Would greatly appreciate help if someone knows a workaround, or a misunderstanding here of course!
Code
Shake.swift
import Foundation
import SwiftUI
struct Shake: Equatable, Identifiable, Hashable {
var name: String
let id = UUID()
static var example = Shake(name: "Banana")
static var many: [Shake] {
return [
Shake(name: "Apple"),
Shake(name: "Banana"),
Shake(name: "Cherry"),
Shake(name: "Berry"),
]
}
}
ShakeDetail.swift
import SwiftUI
struct ShakeDetail: View {
@State var shake: Shake = Shake(name: "")
var body: some View {
List {
TextField("dsf", text: $shake.name, prompt: Text("Enter your favorite shake"))
}
.scrollDismissesKeyboard(.interactively)
.toolbar {
ToolbarItem(placement: .keyboard) {
HStack(alignment: .center) {
Text(shake.name)
.font(.title3)
.foregroundColor(.red)
}
}
}
.toolbarColorScheme(.dark, for: .navigationBar)
.toolbarBackground(
Color.purple,
for: .navigationBar
)
.toolbarBackground(.visible, for: .navigationBar)
}
}
#Preview {
ShakeDetail()
}
ShakeListView.swift
struct ShakeListView: View {
@Binding var shakes: [Shake]
@Binding var selectedShake: Shake?
var body: some View {
List(selection: $selectedShake) {
ForEach(shakes) { shake in
NavigationLink(value: shake) {
Text(shake.name)
.font(.system(.title2, design: .rounded))
.foregroundStyle(.primary)
}
}
}
.navigationTitle("Shakes")
}
}
#Preview {
@Previewable @State var shakes: [Shake] = Shake.many
@Previewable @State var selectedShake: Shake?
ZStack {
ShakeListView(shakes: $shakes, selectedShake: $selectedShake)
}
}
ShakeSplitView.swift
import SwiftUI
import OSLog
public struct ShakeSplitView: View {
@State var selectedShake: Shake?
@State var shakes = Shake.many
@State private var columnVisibility = NavigationSplitViewVisibility.doubleColumn
public init(isShowingFavorites: Bool = false, columnVisibility: SwiftUI.NavigationSplitViewVisibility = NavigationSplitViewVisibility.doubleColumn) {
self.columnVisibility = columnVisibility
}
public var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
ShakeListView(shakes: $shakes, selectedShake: $selectedShake)
} detail: {
if let selectedShake {
// Add shake detail
ShakeDetail(shake: selectedShake)
} else {
Label("Please select a shake", systemImage: "arrow.backward.circle")
.foregroundColor(.purple)
.font(.system(.title2, design: .rounded))
}
}
.navigationSplitViewStyle(.balanced)
}
}
struct FormulaSplitView_Previews: PreviewProvider {
static var previews: some View {
ShakeSplitView()
}
}
ShakeTabView.swift
import SwiftUI
struct ShakeTabView: View {
var body: some View {
/* TabView breaks the ToolbarItem(placement: .keyboard) */
TabView {
ShakeSplitView()
.tabItem {
Image(systemName: "carrot.fill")
Text("Shakes")
}
}
}
}
#Preview {
ShakeTabView()
}
Why is there inconstancy of appearing the keyboard tool bar Item with tab view?
Try to go to second tab and focus the field. Sometimes it does not appear (in my more complex project it does not appear >90% times).
import SwiftUI
struct MainTabView: View {
var body: some View {
TabView {
FirstTabView()
.tabItem { Label("Tab 1", systemImage: "house") }
SecondTabView()
.tabItem { Label("Tab 2", systemImage: "star") }
}
}
}
struct FirstTabView: View {
@State private var text = ""
var body: some View {
NavigationStack {
VStack {
TextField("Enter something 1", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Done") { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
}
}
}
}
}
struct SecondTabView: View {
@State private var text = ""
var body: some View {
NavigationStack {
VStack {
TextField("Enter something 2", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Done") { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
}
}
}
}
}
#Preview {
MainTabView()
}