In the attached code snippet:
struct ContentView: View {
@State private var vText: String = ""
var body: some View {
TextField("Enter text", text: Binding(
get: { vText },
set: { newValue in
print("Text will change to: \(newValue)")
vText = newValue
}
))
}
}
I have access to the newValue of the text-field whenever the text-field content changes, but how do I detect which key was pressed? I can manually get the diff between previous state and the new value to get the last pressed char but is there a simpler way? Also this approach won't let me detect any modifier keys (such as Alt, Ctrl etc) that the user may have pressed.
Is there a pure swift-ui approach to detect these key presses?
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 Apple team and community,
We’re encountering a strange issue with Live Activity that seems related to memory management or background lifecycle.
❓ Issue:
Our app updates a Live Activity regularly (every 3 minutes) using .update(...). However, after the app remains in the background for around 8 hours, the Live Activity reverts to the initial state that was passed into .request(...).
Even though the app continues sending updates in the background, the UI on the Lock Screen and Dynamic Island resets to the original state.
Hello,
In my SwiftUI App i'm trying to create a custom UI trait and a matching bridged SwiftUI environment key. I want to override the environment key in a swift view and then have that reflect in the current UITraitCollection.
I'm following the pattern in the linked video but am not seeing the changes reflect in the current trait collection when I update the swift env value.
I can't find anything online that is helping.
Does anyone know what I am missing?
https://developer.apple.com/videos/play/wwdc2023/10057/
// Setup
enum CustomTheme: String, Codable {
case theme1 = “theme1”,
theme2 = “theme2”
}
struct customThemeTrait: UITraitDefinition {
static let defaultValue = brand.theme1
static let affectsColorAppearance = true
static let identifier = "com.appName.customTheme"
}
extension UITraitCollection {
var customTheme: CustomTheme { self[customThemeTrait.self] }
}
extension UIMutableTraits {
var customTheme: CustomTheme {
get { self[customThemeTrait.self] }
set { self[customThemeTrait.self] = newValue }
}
}
private struct customThemeKey: EnvironmentKey {
static let defaultValue: CustomTheme = .theme1
}
extension customThemeKey: UITraitBridgedEnvironmentKey {
static func read(from traitCollection: UITraitCollection) -> CustomTheme {
traitCollection.customTheme
}
static func write(to mutableTraits: inout UIMutableTraits, value: CustomTheme) {
mutableTraits.customTheme = value
}
}
extension EnvironmentValues {
var customTheme: CustomTheme {
get { self[customThemeKey.self] }
set { self[customThemeKey.self] = newValue }
}
}
// Attempted Usage
extension Color {
static func primaryBackground() -> Color {
UITraitCollection.current.customTheme == .theme1 ? Color.red : Color.blue
}
}
struct ContentView: View {
@State private var theme = .theme1
var body: some View {
if (dataHasLoaded && themeIsSet) {
HomeView()
.environment(\.customTheme, theme)
} else {
SelectThemeView( theme: self.theme, setContentThemeHandler)
}
}
func setContentThemeHandler(theme: customTheme) {
self.theme = theme
}
}
struct HomeView() {
@Environment(\.customTheme) private var currentTheme: customTheme
var body: some View {
VStack {
Text("currentTheme: \(currentTheme.rawValue)")
.background(Color.primaryBackground())
Text("currentUITrait: \(UITraitCollection.current.customTheme.rawValue)")
.background(Color.primaryBackground())
}
}
}
OUTCOME:
After selecting theme2 in the theme selector view and navigating to the homeView, the background is still red and the env and trait values print the following:
currentTheme: theme2
currentUITrait: theme1
Can anyone help me identify what I am missing?
popup window don't react to touch on iOS 18, works fine on iOS 17, this is the code:
import SwiftUI
extension View {
public func popup<Content, Item>(
item: Binding<Item?>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping (Item) -> Content
) -> some View where Content: View, Item: Equatable {
return self.overlay(
PopupWrapper(item: item, onDismiss: onDismiss, content: content)
)
}
}
struct PopupWrapper<Content, Item>: View where Content: View, Item: Equatable {
@Binding var item: Item?
var onDismiss: (() -> Void)?
var content: (Item) -> Content
@State var isAnimating = false
var body: some View {
Group {
if let item {
ZStack {
Color.black
.opacity(0.3)
.ignoresSafeArea()
.contentShape(Rectangle())
.gesture(
TapGesture().onEnded {
withAnimation(.spring(duration: 0.2)) {
isAnimating = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
dismiss()
}
}
}
)
content(item)
.scaleEffect(isAnimating ? 1 : 0.3)
.opacity(isAnimating ? 1 : 0)
.onAppear {
withAnimation(.spring(duration: 0.3)) {
isAnimating = true
}
}
.gesture(
TapGesture().onEnded {
withAnimation(.spring(duration: 0.2)) {
isAnimating = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
dismiss()
}
}
}
)
.onDisappear {
dismiss()
}
}
} else {
EmptyView()
}
}
}
func dismiss() {
item = nil
isAnimating = false
onDismiss?()
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
This sample code exhibits two issues:
struct ContentView: View
{
@State private var myColor = Color.red
var body: some View {
VStack() {
List() {
Text("Object")
Text("Object")
Text("Object")
.listRowSeparatorTint(myColor)
Text("Object")
}
Button(action:{myColor = Color.green})
{Text("Change color")}
}
.foregroundColor(myColor)
}
}
the row separator isn't redraws when the @State property change
listRowSeparatorTint apply to two lines
The first point is really disappointing. Is there anyone which know if this is a bug or there is a more correct way to use listRowSeparatorTint with changing parameter?
Hi everyone,
I'm encountering a persistent build error in a SwiftUI iOS app and I'm running out of ideas.
Setup:
My ContentView uses two @EnvironmentObjects (GameViewModel, SettingsStore). The GameViewModel has an AppState enum (.welcome, .setup, .game). The ContentView body uses a switch viewModel.currentAppState (wrapped in a Group) to display one of three different views (WelcomeView, SetupView, GameView). Navigation between states is triggered by changing viewModel.currentAppState within withAnimation blocks in the respective subviews.
Problem:
I consistently get the build error 'buildExpression' is unavailable: this expression does not conform to 'View' pointing to the lines inside the .setup and .game cases of the switch statement in ContentView.
Code (ContentView.swift - Simplified Test Version that STILL fails):
// Zweck: Steuert die Hauptnavigation basierend auf AppState
// KORRIGIERTE VERSION OHNE .animation(...) am Ende
import SwiftUI
struct ContentView: View {
// Zugriff auf das ViewModel, um den AppState zu lesen
@EnvironmentObject var viewModel: GameViewModel
// SettingsStore wird von untergeordneten Views benötigt
@EnvironmentObject var settingsStore: SettingsStore
var body: some View {
// Optional: Group um das switch-Statement, kann manchmal helfen (kannst du auch weglassen)
Group {
// Wechsle die Ansicht basierend auf viewModel.currentAppState
switch viewModel.currentAppState {
case .welcome:
WelcomeView()
// EnvironmentObjects an WelcomeView übergeben
.environmentObject(viewModel)
.environmentObject(settingsStore)
// Übergangsanimation
.transition(.opacity)
case .setup:
SetupView()
// EnvironmentObjects an SetupView übergeben
.environmentObject(viewModel)
.environmentObject(settingsStore)
// Übergangsanimation
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
case .game:
GameView()
// EnvironmentObjects an GameView übergeben
.environmentObject(viewModel)
.environmentObject(settingsStore)
// Übergangsanimation
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
} // Ende der optionalen Group
// !!! WICHTIG: KEIN .animation(...) Modifier hier !!!
}
}
// Vorschau
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// Erstelle Instanzen für die Vorschau
let vmWelcome = GameViewModel()
vmWelcome.currentAppState = .welcome
let vmSetup = GameViewModel()
vmSetup.currentAppState = .setup
let vmGame = GameViewModel()
vmGame.currentAppState = .game
vmGame.currentCard = Card.defaultCards.first
let settings = SettingsStore()
// Zeige verschiedene Zustände in der Vorschau an
Group {
ContentView()
.environmentObject(vmWelcome)
.environmentObject(settings)
.previewDisplayName("Welcome State")
ContentView()
.environmentObject(vmSetup)
.environmentObject(settings)
.previewDisplayName("Setup State")
ContentView()
.environmentObject(vmGame)
.environmentObject(settings)
.previewDisplayName("Game State")
}
}
}
Troubleshooting Steps Taken (No Success):
Ensured correct placement of .environmentObject modifiers on subviews within the switch.
Removed a previous .animation() modifier applied directly to the switch.
Ensured state changes triggering transitions are wrapped in withAnimation.
Wrapped the switch in a Group.
Multiple "Clean Build Folder".
Deleted entire Derived Data folder (with Xcode closed).
Restarted Xcode and the Mac multiple times.
Deleted and recreated ContentView.swift with the code above.
Crucially: The errors persist even when replacing WelcomeView(), - - - --- SetupView(), and GameView() with simple Text("...") views inside the switch cases (as shown in the code snippet above).
Environment:
Xcode Version: newest
macOS Version: newest
Question:
Does anyone have any idea why the compiler would still fail to type-check this switch structure, even when the views inside are simplified to basic Text? What else could I try to diagnose or fix this? Could it be related to the subviews (SetupView/GameView) potentially having their own NavigationView or complexity, even when replaced by Text in the failing ContentView?
Thanks for any suggestions!
Topic:
UI Frameworks
SubTopic:
SwiftUI
When using FileImporter in SwiftUI, the following error is always returned when closed; even if the user taps "Cancel"
The view service did terminate with error: Error Domain=_UIViewServiceErrorDomain Code=1 "(null)" UserInfo={Terminated=disconnect method}
Recreation rate is 10/10. It feels like a threading issue, but in SwiftUI we are leveraging the .fileImporter modifier, so we cannot hold on to the reference like we would in a class.
Is there a different approach we should be using for this?
Code for recreation
import SwiftUI
struct ContentView: View {
@State private var fileURL: URL?
@State private var showFileImporter: Bool = false
var body: some View {
VStack {
if let fileURL {
Text(fileURL.absoluteString)
}
Button {
showFileImporter = true
} label: {
Text("Select PDF")
}
.fileImporter(
isPresented: $showFileImporter,
allowedContentTypes: [.pdf],
allowsMultipleSelection: true
) { result in
switch result {
case .success(let files):
files.forEach { file in
let gotAccess = file.startAccessingSecurityScopedResource()
if !gotAccess { return }
fileURL = file
file.stopAccessingSecurityScopedResource()
}
case .failure(let error):
print(error)
}
}
}
}
}
I have the following UIViewRepresentable that loads a webview.
struct SViewerWebView: UIViewRepresentable{
var url: String
var token: String
@Binding var isLoading: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let webConfiguration = WKWebViewConfiguration()
let webView = WKWebView(frame:.zero,configuration:webConfiguration)
webView.allowsBackForwardNavigationGestures = true
webView.isInspectable = true
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
guard let urlforRequest = URL(string: url) else {
print("❌ Invalid URL:", url)
return
}
var request = URLRequest(url: urlforRequest)
request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
print("🔄 Loading URL:", url)
print("🛠 Headers:", request.allHTTPHeaderFields ?? [:])
uiView.load(request)
}
//coordinator
class Coordinator: NSObject, WKNavigationDelegate {
var parent: SViewerWebView
init(_ parent: SViewerWebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DispatchQueue.main.async {
self.parent.isLoading = false // Hide loading when page loads
}
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
DispatchQueue.main.async {
self.parent.isLoading = false // Hide loading on error
}
}
}
}
This is the state before the content loads. At this point a ProgressView() is displayed:
The problem comes in the step between screenshot 1 and 3:
as you can see in below pictures, before navigating to the webview content, there is a default loading text that still showing up. Apparently, it seems to be the default behavior from the wkwebview. How can I hide that text status?
my view component:
var body: some View {
ZStack{
SViewerWebView(url: webUrl,token: TokenManager.getToken()!,isLoading: $isLoading)
if isLoading{
VStack {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.scaleEffect(1.5)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.white)
}
}
.ignoresSafeArea()
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
If I present "SFSafariViewController" when a "FamilyActivityPicker" is visible, it will automatically dismiss the "SFSafariViewController" and crash the "FamilyActivityPicker."
I'm assuming the cause of the bug is that each is in a separate process (aside from the app), and there's some hacks to try to stop "FamilyActivityPicker" from crashing, and this is causing the new bug because "SFSafariViewController" is also in a separate process.
(I'm not 100% if its just in 18.4 or iOS 18 overall...)
(I'll try to file a feedback soon, but its 100% reproducible for me across multiple devices on iOS 18.4)
Why there is a working animation with ScrollView + ForEach of items removal, but there is none with List?
ScrollView + ForEach:
struct ContentView: View {
@State var items: [String] = Array(1...5).map(\.description)
var body: some View {
ScrollView(.vertical) {
ForEach(items, id: \.self) { item in
Text(String(item))
.frame(maxWidth: .infinity, minHeight: 50)
.background(.gray)
.onTapGesture {
withAnimation(.linear(duration: 0.1)) {
items = items.filter { $0 != item }
}
}
}
}
}
}
List:
struct ContentView: View {
@State var items: [String] = Array(1...5).map(\.description)
var body: some View {
List(items, id: \.self) { item in
Text(String(item))
.frame(maxWidth: .infinity, minHeight: 50)
.background(.gray)
.onTapGesture {
withAnimation(.linear(duration: 0.1)) {
items = items.filter { $0 != item }
}
}
}
}
}```
Hi,
I got a problem with severe hangs when I use code like this on tvOS 18.2
If I try to use HStack instead of LazyHStack inside the scrollview then the problem does not occur any more but then the scroll performance is compromised and the vertical scroll is no longer that smooth. Does someone has any experience with this? Is this SwiftUI problem or am I missing something?
ScrollView {
LazyVStack {
ForEach(0...100, id: \.self) { _ in
ScrollView {
LazyHStack {
ForEach(0...20, id: \.self) { _ in
Color.red.frame(height: 300)
}
}
}
}
}
}
Hi can you add caching to AsyncImage in the next swift revision
pls see image below.
without caching support it will redownload everything.
wasting api calls
According to the docs tvOS 18+ supports the new NavigationTransition and the matchedTransitionSource and navigationTransition(.zoom(sourceID: id, in: namespace)) modifiers, however they don't seems to work.
Taking the DestinationVideo project example from the latest WWDC the matchedTransitionSourceis marked with #if os(iOS)
Is it supported by tvOS or is it for iOS only?
https://developer.apple.com/documentation/swiftui/view/navigationtransition(_:)
https://developer.apple.com/documentation/swiftui/view/matchedtransitionsource(id:in:configuration:)
I have a Date field that holds the scheduled start date for an activity.. However, activities can be unscheduled (i.e., waiting to be scheduled at some other time). I want to use Date.distantFuture to indicate that the activity is unscheduled. Therefore I am looking to implement logic in my UI that looks something like
@State private var showCalendar: Bool = false
if date == .distantFuture {
Button("unscheduled") {
showCalendar.toggle()
}.buttonStyle(.bordered)
} else {
DatePicker(section: $date)
}
.popover(isPresented: $showCalendar) {
<use DatePicker Calendar view>
}
But this approach requires that I access the DataPicker's Calendar view and I don't know how to do that (and I don't ever what my users to see "Dec 31, 4000"). Any ideas?
(BTW, I have a UIKit Calendar control I could use, but I'd prefer to use the standard control if possible.)
Seeing weird sequences of changes when locking the screen when view is visable.
.onChange(of: scenePhase) { phase in
if phase == .active {
if UIApplication.shared.applicationState == .active {
print("KDEBUG: App genuinely became active")
} else {
print("KDEBUG: False active signal detected")
}
} else if phase == .inactive {
print("KDEBUG: App became inactive")
// Handle inactive state if needed
} else if phase == .background {
print("KDEBUG: App went to background")
// Handle background state if needed
}
}
seen:
(locks screen)
KDEBUG: App became inactive
KDEBUG: App genuinely became active
KDEBUG: App went to background
expected
(locks screen)
KDEBUG: App became inactive
KDEBUG: App went to background
Hi!
I am having issues with my internal testing app now showing up the same through different users devices?
There appears to be a visual bug when using .searchable in a child view that’s pushed via NavigationLink inside a NavigationStack. Specifically, the search bar appears briefly in the wrong position (or animates in an unexpected way) during the transition to the child view.
This issue does not occur when using NavigationView instead of NavigationStack.
Steps to Reproduce:
Create a TabView with a single tab containing a NavigationStack.
Push from a ContentView to a DetailsView using NavigationLink.
Add a .searchable modifier to both the ContentView and DetailsView.
Run the app and tap a row to navigate to the details view.
Expected Behavior
The search bar in the DetailsView should appear smoothly and in the correct position as the view transitions in, just like it does under NavigationView.
Actual Behavior
When the DetailsView appears, the search bar briefly animates or appears in the wrong location before settling into place. This results in a jarring or buggy visual experience.
Feedback: FB17031212
Here is a reddit thread discussing the issue as well https://www.reddit.com/r/SwiftUI/comments/137epji/navigation_stack_with_search_bar_has_a_bug_and_a/
I hope that an Apple engineer can get this fixed soon. It's frustrating to have new APIs come out with the old deprecated yet there are still obvious bugs two years later.
import SwiftUI
public enum Tab {
case main
}
struct AppTabNavigation: View {
@State private var tabSelection = Tab.main
var body: some View {
TabView(selection: $tabSelection) {
NavigationStack {
ContentView()
}
.tag(Tab.main)
.tabItem {
Label("Main", systemImage: "star")
}
}
}
}
struct ContentView: View {
@State private var searchText = ""
var body: some View {
List(0..<100) { i in
NavigationLink("Select \(i)", value: i)
}
.navigationTitle("Main")
.searchable(text: $searchText)
.navigationDestination(for: Int.self) { i in
DetailsView(i: i)
}
}
}
struct DetailsView: View {
@State private var searchText = ""
let i: Int
// MARK: - Body
var body: some View {
List {
ForEach(0..<10, id: \.self) { i in
Text("Hello \(i)")
}
}
.navigationTitle(i.formatted())
.searchable(text: $searchText)
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
When the following structure:
NavigationStack {
ScrollView {
NavigationLink(...)
}
}
is presented inside a sheet in SwiftUI, the scroll drag gesture and the link tap gesture collide.
If the user happens to begin a scroll gesture on a link, the link will open the moment the finger is lifted, no matter how far it is from its initial touchdown.
The issue has already been found (this stack overflow post from 2019), but no solution was provided.
I've already filed a report (FB17034020).
In the meantime, is there any way to override the touch gesture detection in any manner? This issue is quite inconvenient from a user perspective.
import SwiftUI
import OsLog
let logger = Logger(subsystem: "Test", category: "Test")
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
.task {
logger.info("Hallo")
}
}
}
#Preview {
ContentView()
}
27 | .padding()
28 | .task {
29 | logger.info(__designTimeString("#6734_2", fallback: "Hallo"))
| `- error: argument must be a string interpolation
30 | }
31 | }
Should OsLog be compatible with __designTimeString?
My app is a SwiftUI document based app using DocumentGroupLaunchScene. In iOS(iPadOS) 18.4, when it launches, it has duplicate toolbar items, and when I close the current document and open other documents, it adds more duplicates. It also shows a wrong document name, which shows the first opened document name. This issue can be reproduced in the sample code (Building a document-based app with SwiftUI).
I have submitted Feedback (FB17025216), but not sure if this is a known bug or if I'm missing anything.