I have a DocumentGroup working with a FileDocument, and that's fine.
However, when someone creates a new document I want them to have to immediately save it. This is the behavior on ipadOS and iOS from what I can understand (you select where before the file is created).
There seems to be no way to do this on macOS?
I basically want to have someone:
create a new document
enter some basic data
hit "create" which saves the file
then lets the user start editing it
(1), (2), and (4) are done and fairly trivial.
(3) seems impossible, though...?
This really only needs to support macOS but any pointers would be appreciated.
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Posts under SwiftUI tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
In iOS 16, VoiceOver no longer speaks numbers such as “¥1,230" in Japanese, which worked correctly in iOS 15 and earlier. This is a very important bug in Accessibility support.
Steps to reproduce
Create iOS App Project on Xcode. (I used Xcode 14)
Set the Development Localization to "Japanese" on the project settings. (to make VoiceOver speak Japanese)
For example, write the following code.
Build and run the App targeting the actual device, not the simulator.
Actual devices running iOS 15 or earlier will correctly read out loud with VoiceOver, while iOS 16 will have some reading problems.
import SwiftUI
@main
struct VoiceOverProblemApp: App {
var body: some Scene {
WindowGroup {
// on project settings, set the development localization to Japanese.
VStack {
// ✅ said "Hello, world!"
Text("Hello, world!")
// ✅ said "残高 (ざんだか, zan-daka)"
Text("残高")
// ❌ said nothing (until iOS 15 or earlier, said "千二百三十円 (せんにひゃくさんじゅうえん, sen-nihyaku-san-ju-yen)"
Text("¥1,230")
// ❌ said wrong
// correct (iOS 15 or earlier): "残高は千二百三十円です (zan-daka wa sen-nihyaku-san-ju-yen desu)"
// incorrect (iOS 16) : "残高はです (zan-daka wa desu)"
Text("残高は ¥1,230 です")
}
}
}
}
The above sample code uses SwiftUI's Text but the same problem occurs with UIKit's UILabel.
I made an animation in Blender using geometry nodes that I exported to USDC file (then I used Reality Converter to convert to USDZ) and I can see the animation when viewing from the finder but does not play after importing to RCP. Any idea how I can play the animation? Or can the animation be accessed through Xcode?
Thanks!
I have a SwiftUI based app. For lots of reasons I was forced to use NSDocument instead of using DocumentGroup.
I configure the main menu in my AppDelegate.
It has the following code:
let fileMenuItem = NSMenuItem()
let fileMenu = NSMenu(title: "File")
fileMenu.addItem(withTitle: "New", action: #selector(NSDocumentController.newDocument(_:)), keyEquivalent: "n")
fileMenu.addItem(withTitle: "Open...", action: #selector(NSDocumentController.openDocument(_:)), keyEquivalent: "o")
The New and Open work as expected.
It is my understanding that the NSDocumentController should automatically add the "Open Recent" menu when it sees the Open action based on the NSDocumentController.
It is not appearing.
When I print the state of the recent documents using
print("recent documents \(NSDocumentController.shared.recentDocumentURLs), maximum \(NSDocumentController.shared.maximumRecentDocumentCount)")
I see the recent document urls and a count of 10.
What can I do to make the menu appear?
Thanks for the help.
Hi,
I'm trying to fix tvOS view for VoiceOver accessibility feature:
TabView { // 5 tabs
Text(title)
Button(play)
ScrollView { // Live
LazyHStack { 200 items }
}
ScrollView { // Continue watching
LazyHStack { 500 items }
}
}
When the view shows up VoiceOver reads:
"Home tab 1 of 5, Item 2" - not sure why it reads Item 2 of the first cell in scroll view, maybe beacause it just got loaded by LazyHStack.
VocieOver should only read "Home tab 1 of 5"
When moving focus to scroll view it reads:
"Live, Item 1" and after slight delay "Item 1, Item 2, Item 3, Item 4"
When moving focus to second item it reads:
"Item 2" and after slight delay "Item 1, Item 2, Item 3, Item 4"
When moving focus to third item it reads:
"Item 3" and after slight delay "Item 1, Item 2, Item 3, Item 4"
It should be just reading what is focused, idealy just
"Live, Item 1, 1 of 200"
then after moving focus on item 2
"Item 2, 2 of 200"
this time without the word "Live" because we are on the same scroll view (the same horizontal list)
Currently the app is unusable, we have visually impaired testers and this rotor reading everything on the screen is totaly confusing, because users don't know where they are and what is actually focused.
This is a video streaming app and we are streaming all the time, even on home page in background, binge plays one item after another, usually there is never ending Live stream playing, user can switch TV channel, but we continue to play. Voice over should only read what's focused after user interaction.
Original Apple TV app does not do that, so it cannot be caused by some verbose accessibility settings. It reads correctly only focused item in scrolling lists.
How do I disable reading content that is not focused?
I tried:
.accessibilityLabel(isFocused ? title : "")
.accessibilityHidden(!isFocused)
.accessibilityHidden(true) - tried on various levels in view hierarchy
.accessiblityElement(children: .ignore) - even focused item is not read back by voice over
.accessiblityElement(children: .ignore) - even focused item is not read back by voice over
.accessiblityElement(children: .contain) - tried on various levels in view hierarchy
.accessiblityElement(children: .combine) - tried on various levels in view hierarchy
.accessibilityAddTraits(.isHeader) - tried on various levels in view hierarchy
.accessibilityRemoveTraits(.isHeader) - tried on various levels in view hierarchy
// the last 2 was basically an attempt to hack it
.accessibilityRotor("", ranges []) - another hack that I tried on ScrollView, LazyHStack, also on top level view.
50+ other attempts at configuring accessibility tags attached to views.
I have seen all the accessibility videos, tried all sample code projects, I haven't found a solution anywhere, internet search didn't find anything, AI didn't help as it can only provide code that someone else wrote before.
Any idea how to fix this?
Thanks.
There's an easily reproducible SwiftUI bug on macOS where an app's UI state no longer updates/re-renders for "Designed for iPad" apps (i.e. ProcessInfo.processInfo.isiOSAppOnMac == true). The bug occurs in Xcode and also if the app is running independent of Xcode.
The bug occurs when:
the user Hides the app (i.e. it goes into the background)
the user puts the Mac to sleep (e.g. Apple menu > Sleep)
a total of ~60 seconds transpires (i.e. macOS puts the app into the "suspended state")
when the app is brought back into the foreground the UI no longer updates properly
The only way I have found to fix this is to manually open a new actual full app window via File > New, in which case the app works fine again in the new window.
The following extremely simple code in a default Xcode project illustrates the issue:
import SwiftUI
@main
struct staleApp: App {
@State private var isBright = true
var body: some Scene {
WindowGroup() {
ZStack {
(isBright ? Color.white : Color.black).ignoresSafeArea()
Button("TOGGLE") { isBright.toggle(); print("TAPPED") }
}
.onAppear { print("\(isBright ? "light" : "dark") view appeared") }
}
}
}
For the code above, after Hiding the app and putting the computer to sleep for 60 seconds or more, the button no longer swaps views, although the print statements still appear in the console upon tapping the button. Also, while in this buggy state, i can get the view to update to the current state (i.e. the view triggered by the last tap) by manually dragging the corner of the app window to resize the window. But after resizing, the view again does not update upon button tapping until I resize the window again.
so it appears the diff engine is mucked or that the Scene or WindowGroup are no longer correctly running on the main thread
I have tried rebuilding the entire view hierarchy by updating .id() on views but this approach does NOT work. I have tried many other options/hacks but have not been able to reset the 'view engine' other than opening a new window manually or by using: @Environment(.openWindow) private var openWindow
openWindow could be a viable solution except there's no way to programmatically close the old window for isiOSAppOnMac (@Environment(.dismissWindow) private var dismissWindow doesn't work for iOS)
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
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:)
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)
}
}
}
}
}
Hello everyone!
I'm having a problem with background tasks running in the foreground.
When a user enters the app, a background task is triggered. I've written some code to check if the app is in the foreground and to prevent the task from running, but it doesn't always work. Sometimes the task runs in the background as expected, but other times it runs in the foreground, as I mentioned earlier.
Could it be that I'm doing something wrong? Any suggestions would be appreciated.
here is code:
class BackgroundTaskService {
@Environment(\.scenePhase) var scenePhase
static let shared = BackgroundTaskService()
private init() {}
// MARK: - create task
func createCheckTask() {
let identifier = TaskIdentifier.check
BGTaskScheduler.shared.getPendingTaskRequests { requests in
if requests.contains(where: { $0.identifier == identifier.rawValue }) {
return
}
self.createByInterval(identifier: identifier.rawValue, interval: identifier.interval)
}
}
private func createByInterval(identifier: String, interval: TimeInterval) {
let request = BGProcessingTaskRequest(identifier: identifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
scheduleTask(request: request)
}
// MARK: submit task
private func scheduleTask(request: BGProcessingTaskRequest) {
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// some actions with error
}
}
// MARK: background actions
func checkTask(task: BGProcessingTask) {
let today = Calendar.current.startOfDay(for: Date())
let lastExecutionDate = UserDefaults.standard.object(forKey: "lastCheckExecutionDate") as? Date ?? Date.distantPast
let notRunnedToday = !Calendar.current.isDate(today, inSameDayAs: lastExecutionDate)
guard notRunnedToday else {
task.setTaskCompleted(success: true)
createCheckTask()
return
}
if scenePhase == .background {
TaskActionStore.shared.getAction(for: task.identifier)?()
}
task.setTaskCompleted(success: true)
UserDefaults.standard.set(today, forKey: "lastCheckExecutionDate")
createCheckTask()
}
}
And in AppDelegate:
BGTaskScheduler.shared.register(forTaskWithIdentifier: "check", using: nil) { task in
guard let task = task as? BGProcessingTask else { return }
BackgroundTaskService.shared.checkNodeTask(task: task)
}
BackgroundTaskService.shared.createCheckTask()
I am developing a MacOS app with a vertically oriented ScrollView. Inside that View I use a LazyVStack. In the LazyVStack I have a column of images. I want the images to occupy as much of the LazyVStack width as possible so I created a frame for the VStack with a maxWidth: .infinity.
When the user grabs the edge of the window and drags it to reduce the window width, the Views of the window adjust themselves to fit the reduced space. This includes reducing the width of the images. I maintain a fixed aspect ratio for the images, so the height of the images is reduced too.
My problem arises when the width of the images (and height) reduces to the point where the scroll bar is no longer needed because all of the images fit within the height of the ScrollView, and the ScrollView removes the scroll bar. That triggers a redraw of the images, with a slightly bigger size because the width used by the scroll bar is now available. When the the images get bigger they don't all fit within the height of the ScrollView anymore and the scrollbar is restored. That, of course, reduces the space available for the images, so they are reduced in size again - which then triggers the ScrollView to remove the scrollbar again. The whole thing goes into a spasm of flickering images (bigger and smaller) and scrollbar (off and on) until finally I get the dreaded error message:
The window has been marked as needing another Display Window pass, but it has already had more Display Window passes than there are views in the window.
There seems to be no way to prevent this behavior by setting an option on the ScrollView. It would be great if one could indicate that the scroll bar should always remain visible, or at least that the space occupied by the scroll bar remain, even if it is just empty.
The only way I could solve this problem was to go through a somewhat involved calculation to set the image width in such a way that it still responds to window width changes (and some other size changes I allow in some of the other app's Views). This complication would not be necessary if there were better controls on the scroll bar.
Is there a reason for not providing adequate controls for the ScrollView? Its uncontrollable behavior complicates the programming.
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?
I keep running into the following issue when trying to run preview for my application in Xcode.
FailedToLaunchAppError: Failed to launch app.a
/Users/me/Library/Developer/Xcode/DerivedData/a-aloudjuytoewlldqjfxshbjydjeh/Build/Products/Debug/a.app
==================================
| [Remote] JITError
|
| ==================================
|
| | [Remote] XOJITError
| |
| | XOJITError: Could not create code file directory for session: Permission denied`
This is my first post here. Please guide me, if I need to provide more information to answer this post.
I write a simple application, that monitors GPS position (location). I followed Apple documentation for LiveUpdates: https://developer.apple.com/documentation/corelocation/supporting-live-updates-in-swiftui-and-mac-catalyst-apps
My app can monitor location in foreground, background or it can completely stop monitoring location. Background location, if needed, is switched on when application changes scenePhase to .background. But it is in the foreground, that memory leaks occur (according to Instruments/Leaks. Namely Leaks points to the instruction:
let updates = CLLocationUpdate.liveUpdates()
every time I start location and then stop it, by setting updatesStarted to false.
Leaks claims there are 5x leaks there:
Malloc 32 Bytes 1 0x6000002c1d00 32 Bytes libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)
CLDispatchSilo 1 0x60000269e700 96 Bytes CoreLocation 0x184525c64
Malloc 48 Bytes 1 0x600000c8f2d0 48 Bytes Foundation +[NSString stringWithUTF8String:]
NSMutableSet 1 0x6000002c4240 32 Bytes LocationSupport 0x18baa65d4
dispatch_queue_t (serial) 1 0x600002c69c80 128 Bytes libswiftDispatch.dylib OS_dispatch_queue.init(label:qos:attributes:autoreleaseFrequency:target:)
I tried [weak self] in Task, but it doesn't solve the leaks problem and causes other issues, so I dropped it. Anyway, Apple doesn't use it either.
Just in case this is my function, which has been slightly changed comparing to Apple example, to suit my needs:
func startLocationUpdates() {
Task() {
do {
self.updatesStarted = true
let updates = CLLocationUpdate.liveUpdates()
for try await update in updates {
// End location updates by breaking out of the loop.
if !self.updatesStarted {
self.location = nil
self.mapLocation = nil
self.track.removeAll()
break
}
if let loc = update.location {
let locationCoordinate = loc.coordinate
let location2D = CLLocationCoordinate2D(latitude: locationCoordinate.latitude, longitude: locationCoordinate.longitude)
self.location = location2D
if self.isAnchor {
if #available(iOS 18.0, *) {
if !update.stationary {
self.track.append(location2D)
}
} else {
// Fallback on earlier versions
if !update.isStationary {
self.track.append(location2D)
}
}
}
}
}
} catch {
//
}
return
}
}
Can anyone help me locating these leaks?
When I try to implement the new Background Task options in the same way as they show in the WWDC video (on watchOS) likes this:
let config = URLSessionConfiguration.background(withIdentifier: "SESSION_ID")
config.sessionSendsLaunchEvents = true
let session = URLSession(configuration: config)
let response = await withTaskCancellationHandler {
try? await session.data(for: request)
} onCancel: {
let task = session.downloadTask(with: request))
task.resume()
}
I'm receiving the following error:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
Did I forget something?
I have a memory leak, when using AVAudioPlayer. I managed to narrow down the issue into a very simple app, which code I paste in at the end.
The memory leak start immediately when I start playing sound, but only in the emylator. On the real iPhone there is no memory leak.
The memory leak on the Simulator looks like this:
import SwiftUI
import AVFoundation
struct ContentView_Audio: View {
var sound: AVAudioPlayer?
init() {
guard let path = Bundle.main.path(forResource: "cd201", ofType: "mp3") else { return }
let url = URL(fileURLWithPath: path)
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers])
} catch {
return
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch {
return
}
do {
sound = try AVAudioPlayer(contentsOf: url)
} catch {
return
}
}
var body: some View {
HStack {
Button {
playSound()
} label: {
ZStack {
Circle()
.fill(.mint.opacity(0.3))
.frame(width: 44, height: 44)
.shadow(radius: 8)
Image(systemName: "play.fill")
.resizable()
.frame(width: 20, height: 20)
}
}
.padding()
Button {
stopSound()
} label: {
ZStack {
Circle()
.fill(.mint.opacity(0.3))
.frame(width: 44, height: 44)
.shadow(radius: 8)
Image(systemName: "stop.fill")
.resizable()
.frame(width: 20, height: 20)
}
}
.padding()
}
}
private func playSound() {
guard sound != nil else { return }
sound?.volume = 1
// sound?.numberOfLoops = -1
sound?.play()
}
func stopSound() {
sound?.stop()
}
}
Issue Description
Whenever the first item in the List is a DisclosureGroup, all subsequent disclosure groups work fine. However, if the first item is not a disclosure group, the disclosure groups in subsequent items do not render correctly.
This issue does not occur in macOS 15, where everything works as expected.
Has anyone else encountered this behavior, or does anyone have a workaround for macOS 13 & 14?
I’m not using OutlineGroup because I need to bind to an isExpanded property for each row in the list.
Reproduction Steps
I’ve created a small test project to illustrate the issue:
Press “Insert item at top” to add a non-disclosure item at the start of the list.
Then, press “Append item with sub-item” to add a disclosure group further down.
The disclosure group does not display correctly. The label of the disclosure group renders fine, but the content of the disclosure group does not display at all.
Press "Insert item at top with sub-item" and the list displays as expected.
Build Environment
macOS 15.3.2 (24D81)
Xcode Version 16.2 (16C5032a)
Issue Observed
macOS 13 & 14 (bug occurs)
macOS 15 (works correctly)
Sample Code
import SwiftUI
class ListItem: ObservableObject, Hashable, Identifiable {
var id = UUID()
@Published var name: String
@Published var subItems: [ListItem]?
@Published var isExpanded: Bool = true
init(
name: String,
subjobs: [ListItem]? = nil
) {
self.name = name
self.subItems = subjobs
}
static func == (lhs: ListItem, rhs: ListItem) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
struct ContentView: View {
@State private var listItems: [ListItem] = []
@State private var selectedJob: ListItem?
@State private var redraw: Int = 0
var body: some View {
VStack {
List(selection: $selectedJob) {
ForEach(self.listItems, id: \.id) { job in
self.itemRowView(for: job)
}
}
.id(redraw)
Button("Insert item at top") {
self.listItems.insert(
ListItem(
name: "List item \(listItems.count)"
),
at: 0
)
}
Button("Insert item at top with sub-item") {
self.listItems.insert(
ListItem(
name: "List item \(listItems.count)",
subjobs: [ListItem(name: "Sub-item")]
),
at: 0
)
}
Button("Append item") {
self.listItems.append(
ListItem(
name: "List item \(listItems.count)"
)
)
}
Button("Append item with sub-item") {
self.listItems.append(
ListItem(
name: "List item \(listItems.count)",
subjobs: [ListItem(name: "Sub-item")]
)
)
}
Button("Clear") {
self.listItems.removeAll()
}
Button("Redraw") {
self.redraw += 1
}
}
}
@ViewBuilder
private func itemRowView(for job: ListItem) -> some View {
if job.subItems == nil {
self.itemLabelView(for: job)
} else {
AnyView(
erasing: ListItemDisclosureGroup(job: job) {
self.itemLabelView(for: job)
} jobRowView: { child in
self.itemRowView(for: child)
}
)
}
}
@ViewBuilder private func itemLabelView(for job: ListItem) -> some View {
Text(job.name)
}
struct ListItemDisclosureGroup<LabelView: View, RowView: View>: View {
@ObservedObject var job: ListItem
@ViewBuilder let labelView: () -> LabelView
@ViewBuilder let jobRowView: (ListItem) -> RowView
var body: some View {
DisclosureGroup(isExpanded: $job.isExpanded) {
if let children = job.subItems {
ForEach(children, id: \.id) { child in
self.jobRowView(child)
}
}
} label: {
self.labelView()
}
}
}
}
When i have TextField inside ScrollView and tap on it the keyboard is shown as expected. But it seems that the TextField is moved up just enough to show the input area but i want to be moved enough so that is visible in its whole. Otherwise it looks cropped. I couldn't find a way to change this behaviour.
struct ContentView: View {
@State var text:String = ""
var body: some View {
ScrollView {
VStack(spacing: 10) {
ForEach(1...12, id: \.self) {
Text("\($0)…")
.frame(height:50)
}
TextField("Label..", text: self.$text)
.padding(10)
.background(.white)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(.blue, lineWidth: 1)
)
}
.padding()
.background(.red)
}
}
}
Dear Developers and DTS team,
This is writing to seek your expert guidance on a persistent memory leak issue I've discovered while implementing video playback in a SwiftUI application.
Environment Details:
iOS 17+, Swift (SwiftUI, AVKit), Xcode 16.2
Target Devices:
iPhone 15 Pro (iOS 18.3.2)
iPhone 16 Plus (iOS 18.3.2)
Detailed Issue Description:
I am experiencing consistent memory leaks when using UIViewControllerRepresentable with AVPlayerViewController for FullscreenVideoPlayer and native VideoPlayer during video playback termination.
Code Context:
I have implemented the following approaches:
Added static func dismantleUIViewController(: coordinator:)
Included deinit in Coordinator
Utilized both UIViewControllerRepresentable and native VideoPlayer
/// A custom AVPlayer integrated with AVPlayerViewController for fullscreen video playback.
///
/// - Parameters:
/// - videoURL: The URL of the video to be played.
struct FullscreenVideoPlayer: UIViewControllerRepresentable {
// @Binding something for controlling fullscreen
let videoURL: URL?
func makeUIViewController(context: Context) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.delegate = context.coordinator
print("AVPlayerViewController created: \(String(describing: controller))")
return controller
}
/// Updates the `AVPlayerViewController` with the provided video URL and playback state.
///
/// - Parameters:
/// - uiViewController: The `AVPlayerViewController` instance to update.
/// - context: The SwiftUI context for updates.
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {
guard let videoURL else {
print("Invalid videoURL")
return
}
// Initialize AVPlayer if it's not already set
if uiViewController.player == nil || uiViewController.player?.currentItem == nil {
uiViewController.player = AVPlayer(url: videoURL)
print("AVPlayer updated: \(String(describing: uiViewController.player))")
}
// Handle playback state
}
func makeCoordinator() -> Coordinator {
Coordinator(parent: self)
}
static func dismantleUIViewController(_ uiViewController: AVPlayerViewController, coordinator: Coordinator) {
uiViewController.player?.pause()
uiViewController.player?.replaceCurrentItem(with: nil)
uiViewController.player = nil
print("dismantleUIViewController called for \(String(describing: uiViewController))")
}
}
extension FullscreenVideoPlayer {
class Coordinator: NSObject, AVPlayerViewControllerDelegate {
var parent: FullscreenVideoPlayer
init(parent: FullscreenVideoPlayer) {
self.parent = parent
}
deinit {
print("Coordinator deinitialized")
}
}
}
struct ContentView: View {
private let videoURL: URL? = URL(string: "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4")
var body: some View {
NavigationStack {
Text("My Userful View")
List {
Section("VideoPlayer") {
NavigationLink("FullscreenVideoPlayer") {
FullscreenVideoPlayer(videoURL: videoURL)
.frame(height: 500)
}
NavigationLink("Native VideoPlayer") {
VideoPlayer(player: .init(url: videoURL!))
.frame(height: 500)
}
}
}
}
}
}
Reproducibility Steps:
Run application on target devices
Scenario A - FullscreenVideoPlayer:
Tap FullscreenVideoPlayer
Play video to completion
Repeat process 5 times
Scenario B - VideoPlayer:
Navigate back to main screen
Tap Video Player
Play video to completion
Repeat process 5 times
Observed Memory Leak Characteristics:
Per Iteration (Debug Memory Graph):
4 instances of NSMutableDictionary (Storage) leaked
4 instances of __NSDictionaryM leaked
4 × 112-byte malloc blocks leaked
Cumulative Effects:
Debug console prints: "dismantleUIViewController called for <AVPlayerViewController: 0x{String}> Coordinator deinitialized" when navigate back to main screen
After multiple iterations, leak instances double
Specific Questions:
What underlying mechanisms are causing these memory leaks in UIViewControllerRepresentable and VideoPlayer?
What are the recommended strategies to comprehensively prevent and resolve these memory management issues?
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.)