Here is a simple main.swift file of a macOS app:
import SwiftUI
struct ContentView: View {
@State private var selectedItem = 0
@FocusState private var isListFocused: Bool
var body: some View {
List(0..<40, id: \.self, selection: $selectedItem) { index in
Text("\(index)")
.padding()
.focusable()
}
.focused($isListFocused)
.onAppear {
isListFocused = true
}
}
}
func createAppWindow() {
let window = NSWindow(
contentRect: .zero,
styleMask: [.titled],
backing: .buffered,
defer: false
)
window.contentViewController = NSHostingController(rootView: ContentView())
window.setContentSize(NSSize(width: 759, height: 300))
window.center()
window.makeKeyAndOrderFront(nil)
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
createAppWindow()
}
}
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
NSApplication.shared.run()
Try to move the focus with a keyboard slowly as shown on the GIF attached and you'll see that the focus items don't sit in a List's view.
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
No real intruduction for this, so I'll get to the point:
All this code is on GitHub: https://github.com/the-trumpeter/Timetaber-for-iWatch
But first, sorry;
/*
I got roasted,
last time I posted;
for not defining my stuff.
This'll be different,
but's gonna be rough;
'cuz there's lots and lots
to get through:
*/
//this is 'Timetaber Watch App/Define (No expressions)/Courses_vDef.swift' on the GitHub:
struct Course {
let name: String
let icon: String
let room: String
let colour: String
let listName: String
let listIcon: String
let joke: String
init(name: String, icon: String, room: String? = nil, colour: String,
listName: String? = nil, listIcon: String? = nil, joke: String? = nil)
{
self.name = name
self.icon = icon
self.room = room ?? "None"
self.colour = colour
self.listName = listName ?? name
self.listIcon = listIcon ?? (icon+".circle.fill")
self.joke = joke ?? ""
}
}
//this is 'Timetaber Watch App/TimeManager_fDef.swift' on the GitHub:
func getCurrentClass(date: Date) -> Array<Course> {
//returns the course in session depending on the input date
//it is VERY long but
//all you really need to know is what it returns:
//basically: return [rightNow, nextUp]
}
/*
I thought that poetry
would be okay,
But poorly thought things through:
For I'll probably find
that people online
will treat my rhymes like spew.
*/
So into the question:
I have a bunch of views, all (intendedly) watching two variables inside of a class:
//Github: 'Timetaber Watch App/TimetaberApp.swift'
class GlobalData: ObservableObject {
@Published var currentCourse: Course = getCurrentClass(date: .now)[0] // the current timetabled class in session.
@Published var nextCourse: Course = getCurrentClass(date: .now)[1] // the next timetabled class in session
}
...and a bunch of views using them in different ways as follows:
(Sorry, don't have the characters to define functions called in these)
import SwiftUI
//Github: 'Timetaber Watch App/Views/HomeView.swift'
struct HomeView: View {
@StateObject var data = GlobalData()
var body: some View {
//HERE:
let icon = data.currentCourse.icon
let name = data.currentCourse.name
let colour = data.currentCourse.colour
let room = roomOrBlank(course: data.currentCourse)
let next = data.nextCourse
VStack {
//CURRENT CLASS
Image(systemName: icon)
.foregroundColor(Color(colour))//add an SF symbol element
.imageScale(.large)
.font(.system(size: 25).weight(.semibold))
Text(name)
.font(.system(size:23).weight(.bold))
.foregroundColor(Color(colour))
.padding(.bottom, 0.1)
//ROOM
Text(room+"\n")
.multilineTextAlignment(.center)
.foregroundStyle(.gray)
.font(.system(size: 15))
if next.name != noSchool.name {
Spacer()
//NEXT CLASS
Text(nextPrefix(course: next))
.font(.system(size: 15))
Text(getNextString(course: next))
.font(.system(size: 15))
.multilineTextAlignment(.center)
}
}.padding()
}
}
// Github: 'Timetaber Watch App/Views/ListView.swift'
struct listTemplate: View {
@StateObject var data = GlobalData()
var listedCourse: Course = failCourse(feedback: "lT.12")
var courseTime: String = ""
init(course: Course, courseTime: String) {
self.courseTime = courseTime
self.listedCourse = course
}
var body: some View {
let localroom = if listedCourse.room == "None" {
"" } else { listedCourse.room }
let image = if listedCourse.listIcon == "custom1" {
Image(.paintbrushPointedCircleFill)
} else { Image(systemName: listedCourse.listIcon) }
HStack{
image
.foregroundColor(Color(listedCourse.colour))
.padding(.leading, 5)
Text(listedCourse.name)
.bold()
Spacer()
Text(courseTime)
Text(localroom).bold().padding(.trailing, 5)
}
.padding(.bottom, 1)
.background(data.currentCourse.name==listedCourse.name ? Color(listedCourse.colour).colorInvert(): nil) //HERE
}
}
struct listedDay: View {
let day: Dictionary<Int, Course>
var body: some View {
let dayKeys = Array(day.keys).sorted(by: <)
List {
ForEach((0...dayKeys.count-2), id: \.self) {
let num = $0
listTemplate(course: day[dayKeys[num]] ?? failCourse(feedback: "lD.53"), courseTime: time24toNormal(time24: dayKeys[num]))
}
}
}
}
struct ListView: View {
var body: some View {
if storage.shared.termRunningGB && weekdayFunc(inDate: .now) != 1
&& weekdayFunc(inDate: .now) != 7 {
ScrollView {
listedDay(
day: getTimetableDay(
isWeekA:
getIfWeekIsA_FromDateAndGhost(
originDate: .now,
ghostWeek: storage.shared.ghostWeekGB
),
weekDay: weekdayFunc(inDate: .now)
)
)
}
} else if !storage.shared.termRunningGB {
Text("There's no term running.\nThe day's classes will be displayed here.")
.multilineTextAlignment(.center)
.foregroundStyle(.gray)
.font(.system(size: 13))
} else {
Text("No school today.\nThe day's classes will be displayed here.")
.multilineTextAlignment(.center)
.foregroundStyle(.gray)
.font(.system(size: 13))
}
}
}
//There's one more view but I can't fit it for characters.
//On GitHub: 'Timetaber Watch App/Views/SettingsView.swift'
So...
THE FUNCTION:
This function is called when changes are made that will affect the correct output of getCurrentClass. It is intended to reload the views and the current/next variables to reflect those changes.\
//GHub: 'Timetaber Watch App/StorageManager.swift'
func reload() -> Void {
@ObservedObject var globalData: GlobalData //this line is erroring, I don't know how to fix it. Is this even the best/proper way to do this?
let courseData = getCurrentClass(date: .now)
globalData.currentCourse = courseData[0]
globalData.nextCourse = courseData[1]
//Variable '_globalData' used by function definition before being initialized
//that is the error appearing on those above two redefinitions.
print("Setup done\n")
}
Thanks!
-Gill
Summary:
At WWDC24, a new transition was introduced by the Apple Design team (.contentTransition(.symbolEffect(.replace)))
I was writing a post about it on my LinkedIn (https://www.linkedin.com/in/alex-fila/), and out of curiosity I tried multiple symbols with slashes. Many of them were not well center aligned during a new symbol effect. Some of the examples are: "speaker.fill" : "speaker.slash.fill”, "eye.fill" : "eye.slash.fill”. Please check the attached Swift file for more details and full SwiftUI View with issues.
Steps to Reproduce:
Create a new IOS App project in XCode.
Create a new SwiftUI File.
Initiate state variable: @State private var isSpeakerOn = true.
Create a new image with transition:
Image(systemName: isSpeakerOn ? "speaker.fill" : "speaker.slash.fill")
.contentTransition(.symbolEffect(.replace)).
5. Create a switcher or set a timer with a constant variable to toggle isSpeakerOn value (see attachment file).
6. Toggle isSpeakerOn value.
7. Observe the issue (2 symbols are not well center aligned during transition).
Expected Results:
During transition .contentTransition(.symbolEffect(.replace)) 2 SF symbols ("speaker.fill" : "speaker.slash.fill”) are well center aligned.
Actual Results:
During transition (when slash slowly appears on top of SF symbol), the main symbol is moved a few points up, creating a decentralized effect and making the user experience feel inconsistent.
Notes:
There are 200 SF Symbols with .slash that might be affected. It happens on latest Xcode and macOS versions, and could be a top priority for the Apple Design Team.
import SwiftUI
struct BUG: View {
@State private var isSpeakerOn = true
let timer = Timer.publish(every: 1.5, on: .main, in: .common).autoconnect()
let columns = [
GridItem(.flexible(), spacing: 20),
GridItem(.flexible(), spacing: 20)
]
var body: some View {
LazyVGrid(columns: columns, spacing: 60) {
Text("❌").font(.system(size: 100))
Image(systemName: isSpeakerOn ? "speaker.fill" : "speaker.slash.fill")
.font(.system(size: 200))
.frame(width: 200, height: 100, alignment: .center)
.contentTransition(.symbolEffect(.replace))
.symbolRenderingMode(.palette)
.foregroundStyle(
Color.primary,
Color.accentColor)
.onReceive(timer) { _ in
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {isSpeakerOn.toggle()}}
Text("✅").font(.system(size: 100))
Image(systemName: isSpeakerOn ? "bell.fill" : "bell.slash.fill")
.font(.system(size: 170))
.frame(width: 150, height: 150, alignment: .center)
.contentTransition(.symbolEffect(.replace))
.symbolRenderingMode(.palette)
.foregroundStyle(
Color.primary,
Color.accentColor)
.onReceive(timer) { _ in
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {isSpeakerOn.toggle()}}
Text("❌").font(.system(size: 100))
Image(systemName: isSpeakerOn ? "eye.fill" : "eye.slash.fill")
.font(.system(size: 150))
.frame(width: 200, height: 100, alignment: .center)
.contentTransition(.symbolEffect(.replace))
.symbolRenderingMode(.palette)
.foregroundStyle(
Color.primary,
Color.accentColor)
.onReceive(timer) { _ in
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {isSpeakerOn.toggle()}}
}
.padding(40)
}}
#Preview { BUG() }
I'm trying to implement the same UI used by the Settings app on iPad: a split view with two columns that are visible at all times.
This code produces the layout i want, but I would like to hide the "toggle sidebar visibility" button that the system introduces.
Is there a SwiftUI API I can use to hide this button? Maybe an alternate way to setup views that tells the system that the button is not necessary?
struct SomeView: View {
var body: some View {
NavigationSplitView(
columnVisibility: .constant(.all),
sidebar: { Text("sidebar") },
detail: { Text("detail") }
)
.navigationSplitViewStyle(.balanced)
}
}
I'm implementing a Map with user location customization in SwiftUI using iOS 17+ MapKit APIs. When using the selection parameter with Map, the default blue dot user location becomes tappable but shows an empty annotation view. However, using UserAnnotation makes the location marker non-interactive.
My code structure:
import SwiftUI
import MapKit
struct UserAnnotationSample: View {
@State private var position: MapCameraPosition = .userLocation(fallback: .automatic)
@State private var selectedItem: MapSelection<MKMapItem>?
var body: some View {
Map(position: $position, selection: $selectedItem) {
// UserAnnotation()
}
.mapControls {
MapUserLocationButton()
}
}
}
Key questions:
How can I replace the empty annotation view with a custom avatar when tapping the user location?
Is there a way to make UserAnnotation interactive with selection?
Should I use tag modifier for custom annotations? What's the proper way to associate selections?
I'd like to create a custom SwiftUI view that supports extracting its title string along with the localization comment into a string catalog. Like the SwiftUI Text view does. I have a view with an init similar to the localization init of Text. But it looks like I'm missing something obvious.
Two questions:
How do I get the actual localized string using a LocalizedStringKey?
Why is the comment not picked up and added to the string catalog?
// 1) My custom view with localization support:
// I'd like to build a view which supports extraction of strings into a string catalog like the SwiftUI `Text` view does.
struct MyLocalizableView: View {
private var localizedTitle: String
init (_ titleKey: LocalizedStringKey, table: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil) {
// PROBLEM I:
// The following line does not work. I is a fantasy call. It depicts my idea how I would expect it to work.
// My question is: How do I get the actual localized string using a `LocalizedStringKey`?
self.localizedTitle = String(localizedKey: titleKey, table: table, bundle: bundle, comment: comment)
}
var body: some View {
// At this point I want to do an operation on an actual string and not on a LocalizedStringKey. So I can't just pass the LocalizedStringKey value along.
// Do `isEmpty` or some other operation on an actual string:
if localizedTitle.isEmpty {
Text("Show one thing")
} else {
Text("Show another thing")
Text("** \(localizedTitle) **")
}
}
}
// 2) The call site:
struct ContentView: View {
var body: some View {
// PROBLEM II: "My title key" is picked up and is extracted into the string catalog of the app. But the comment is NOT!
MyLocalizableView("My title key", comment: "The title of the view...")
.padding()
}
}
Whenever I start editing TextField or while editing TextField, Xcode shows this worning, and takes a few seconds to show the keyboard.
There is no 'availabilityDetailedInfo' in my source code, and I could not find similar errors on the internet.
Can't find or decode availabilityDetailedInfo
unavailableReasonsHelper: Failed to get or decode availabilityDetailedInfo
Can't find or decode reasons
unavailableReasonsHelper: Failed to get or decode unavailable reasons as well
Can't find or decode availabilityDetailedInfo
unavailableReasonsHelper: Failed to get or decode availabilityDetailedInfo
Can't find or decode reasons
unavailableReasonsHelper: Failed to get or decode unavailable reasons as well
import SwiftUI
struct Product: Identifiable {
let id = UUID()
let name: String
let pricePerKg: Double
}
struct ContentView: View {
@State private var selectedProduct: Product?
@State private var quantity: Double = 1.0
@State private var orderDate = Date()
@State private var showingConfirmation = false
let products = [
Product(name: "Lamb", pricePerKg: 15.0),
Product(name: "Beef", pricePerKg: 20.0),
Product(name: "Chicken", pricePerKg: 10.0)
]
var body: some View {
NavigationView {
Form {
Section(header: Text("Select Meat")) {
Picker("Meat Type", selection: $selectedProduct) {
ForEach(products) { product in
Text(product.name).tag(product as Product?)
}
}
}
if let selectedProduct = selectedProduct {
Section(header: Text("Quantity (kg)")) {
Stepper(value: $quantity, in: 0.5...10, step: 0.5) {
Text("\(quantity, specifier: "%.1f") kg")
}
}
Section(header: Text("Delivery Date")) {
DatePicker("Select Date", selection: $orderDate, in: Date()..., displayedComponents: .date)
}
Section(header: Text("Total Price")) {
Text("$\(selectedProduct.pricePerKg * quantity, specifier: "%.2f")")
}
Button("Confirm Order") {
showingConfirmation = true
}
.alert(isPresented: $showingConfirmation) {
Alert(title: Text("Order Confirmed"), message: Text("You have ordered \(quantity, specifier: "%.1f") kg of \(selectedProduct.name) for \(orderDate.formatted(date: .long, time: .omitted))."), dismissButton: .default(Text("OK")))
}
}
}
.navigationTitle("Halal Butcher")
}
}
}
@main
struct HalalButcherApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Hey,
It seems that when Apple Intelligence is enabled, scrolling can become completely broken when using an app. This is affecting several apps, including telegram:
https://github.com/TelegramMessenger/Telegram-iOS/issues/1570?reload=1
It seems that UIPanGesture is affected by this. (MapKit stop being able to scroll too).
Killing + Relaunching the app fix the problem.
Bug report ID, containing a video: FB16780431
I am developing an app in swiftUI using Xcode 12.3, deployment target iOS 14.0. The launch screen is setup through info.plist by specifying 'background color' and 'image name'. The file used in 'image name' is from Assets catalog. (PNG format, size300 x 300 and corresponding @2x and @3x resolutions) What I have observed, when the app is installed for the first time the launch image is centered and have original resolutions but all subsequent launches show launch images stretched to cover full screen. Any ideas why this is happening and how to have more consistent behavior either way?
I have tried 'respect safe area' option but it does not make a difference.
Thank you.
I have a NSViewController as the root view and have a switui view embedded in it via NSHostingView.
override func loadView() {
self.view = NSHostingView(rootView: SwiftUiView())
}
}
In the SwiftUiView, I have a TextField and an NSTextView embedded using NSViewRepresentable, along with a few buttons. There is also a menu:
Menu {
ForEach(menuItems), id: \.self) { item in
Button {
buttonClicked()
} label: {
Text(item)
}
}
} label: {
Image("DropDown")
.contentShape(Rectangle())
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
}
The NSTextView and TextField work fine, and I can type in them until I click on the menu or show an alert. After that, I can no longer place my cursor in the text fields. I am able to select the text but not type in it. When I click on the NSTextView or TextField, nothing happens.
At first, I thought it was just a cursor visibility issue and tried typing, but I received an alert sound. I've been trying to fix this for a couple of days and haven't found any related posts. Any help would be greatly appreciated.
The issue is, I cannot auto acquire bluetooth keyboard focus in PHPickerViewController after enabling 'Full Keyboard Access' in my IPhone 14 with iOS version 18.3.1. The keyboard focus in PHPickerViewController will show, however, after I tapped on the blank space of the PHPickerViewController. How to make the focus on at the first place then?
I'm using UINavigationController and calling setNavigationBarHidden(true, animated: false). Then I use this controller to present PHPickerViewController using some configuration setup below.
self.configuration = PHPickerConfiguration()
configuration.filter = .any(of: filters)
configuration.selectionLimit = selectionLimit
if #available(iOS 15.0, *), allowOrdering {
configuration.selection = .ordered
}
configuration.preferredAssetRepresentationMode = .current
Finally I set the delegate to PHPickerViewController and call UINavigationController.present(PHPickerViewController, animated: true) to render it.
Also I notice animation showing in first video then disappear.
This is an issue that occurred while using SwiftUI.
Cannot find '$state' in scope
The other view finds properties normally.
May I know why the error is occurring?
The following code is the full text of the code that causes problems.
import SwiftUI
@Observable
class HomeState {
var title: String = "Home"
}
struct HomeView: View {
@Binding var state: HomeState
var body: some View {
Text(state.title)
}
}
#Preview {
@Previewable @State var state: HomeState = .init()
HomeView(state: $state) /// Error: Cannot find '$state' in scope
}
The same error occurs when using the String type rather than the object.
What did I do wrong?
I have a simple SwiftUI project with two basic build configurations (Debug, Release) as shown below.
I now choose Build > Scheme > Edit Scheme under Product and select Release as the current build configuration as shown below.
And the Preview canvas exhibit errors.
If I click on the Diagnostics button, it says under PREVIEW UPDATE ERROR
OptimizationLevelError: not building -Onone
”BuildSchemeCrazyDaughter.app” needs -Onone Swift optimization level to use previews (current setting is -O)
What does that mean and why don't I get the preview for the Release build configuration? Thanks.
Perhaps I just have the wrong expectations, but I discovered some odd behavior from SwiftData that sure seems like a bug to me...
If you make any change to any SwiftData model object — even just setting a property to its current value — every SwiftUI view that uses SwiftData is rebuilt. Every query and every entity reference, even if the property was set on a model class that is completely unrelated to the view.
SwiftUI does such a good job of optimizing UI updates that it's hard to notice the issue. I only noticed it because the updates were triggering my debug print statements.
To double-check this, I went back to Apple's new iOS app template — the one that is just a list of dated items — and added a little code to touch an unrelated record in the background:
@Model
class UnrelatedItem {
var name: String
init(name: String) {
self.name = name
}
}
@main
struct jumpyApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
UnrelatedItem.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
init() {
let context = sharedModelContainer.mainContext
// Create 3 items at launch so we immediately have some data to work with.
if try! context.fetchCount(FetchDescriptor<Item>()) == 0 {
for _ in 0..<3 {
let item = Item(timestamp: Date())
context.insert(item)
}
}
// Now create one unrelated item.
let unrelatedItem = UnrelatedItem(name: "Mongoose")
context.insert(unrelatedItem)
try? context.save()
// Set up a background task that updates the unrelated item every second.
Task {
while true {
try? await Task.sleep(nanoseconds: 1_000_000_000)
Task { @MainActor in
// We don't even have to change the name or save the contxt.
// Just setting the name to the same value will trigger a change.
unrelatedItem.name = "Mongoose"
}
}
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
I also added a print statement to the ContentView so I could see when the view updates.
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
List {
let _ = Self._printChanges()
...
The result is that the print statement logs 2 messages to the debug console every second. I checked in iOS 17, 18.1, and 18.2, and they all behave this way.
Is this the intended behavior? I thought the whole point of the new Observation framework in iOS 17 was to track which data had changed and only send change notifications to observers who were using that data.
I am trying to implement "Live activity" to my app. I am following the Apple docs.
Link: https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities
Example code:
struct LockScreenLiveActivityView: View {
let context: ActivityViewContext<PizzaDeliveryAttributes>
var body: some View {
VStack {
Spacer()
Text("\(context.state.driverName) is on their way with your pizza!")
Spacer()
HStack {
Spacer()
Label {
Text("\(context.attributes.numberOfPizzas) Pizzas")
} icon: {
Image(systemName: "bag")
.foregroundColor(.indigo)
}
.font(.title2)
Spacer()
Label {
Text(timerInterval: context.state.deliveryTimer, countsDown: true)
.multilineTextAlignment(.center)
.frame(width: 50)
.monospacedDigit()
} icon: {
Image(systemName: "timer")
.foregroundColor(.indigo)
}
.font(.title2)
Spacer()
}
Spacer()
}
.activitySystemActionForegroundColor(.indigo)
.activityBackgroundTint(.cyan)
}
}
Actually, the code is pretty straightforward. We can use the timerInterval for count-down animation. But when the timer ends, I want to update the Live Activity view. If the user re-opens the app, I can update it, but what happens if the user doesn't open the app? Is there a way to update the live activity without using push notifications?
Using, the standard Apple example at https://developer.apple.com/documentation/swiftui/building-a-document-based-app-with-swiftui
I only made a small change to print when reading a file, with the time.
When you use 'revert to saved', it writes the current version (expected), then loads the saved version (expected), then a few seconds later (not moving the mouse, edits, etc.) it reloads the document again. Then if you click away from the window, it loads it yet again - four times!
This loading of the document twice breaks apps where the loading may take longer (large documents), then the document is replaced while the user has already started editing the recently loaded document.
This is a really bad bug. Any ideas?
Here is the added logs:
reading file! testfile.story at 2025-03-11 20:35:16 +0000
saving file! testfile.story at 2025-03-11 20:35:27 +0000
reading file! testfile.story at 2025-03-11 20:35:27 +0000
reading file! testfile.story at 2025-03-11 20:35:30 +0000
reading file! testfile.story at 2025-03-11 20:35:31 +0000
I see the same behavior with 'Revert To Last Opened'. It seems to work as expected when you browse all versions and pick a specific version.
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
LazyVStack(spacing: 0) {
ForEach(0..<10000) { index in
// If VStack remove, memory issue occur
// VStack {
CustomView(index: index)
// }
}
}
}
}
}
struct CustomView: View {
var index: Int
var body: some View {
VStack {
Text("\(index)")
}
}
}
I wrapped it into a shorter and simpler version, but it still works.
At first, I struggled to figure out why the initial code was causing lag. After investigating with the Debug Memory Graph, I found that the generated custom view’s memory was not being released properly.
This seemed strange because I was using the custom view inside a LazyHStack.
So, I tried various approaches to resolve the issue.
In the Debug Memory Graph, I started suspecting that SwiftUI’s built-in views like VStack and HStack might be affecting memory management. To test this, I wrapped my custom view inside a VStack, and the memory issue disappeared.
However, I want to understand why I need to include the custom view inside a VStack for proper memory management.
(I simplified this code by wrapping it into a shorter version. However, in a real project, the custom view is more complex, and the data list contains more than 10,000 items. This caused severe lag.)
xcode: 16.2, iOS 18, iOS 16
In a UIKit application, removing a view from the hierarchy is straightforward—we simply call myView.removeFromSuperview(). This not only removes myView from the UI but also deallocates any associated memory.
Now that I'm transitioning to SwiftUI, I'm struggling to understand the recommended way to remove a view from the hierarchy, given SwiftUI's declarative nature.
I understand that in SwiftUI, we declare everything that should be displayed. However, once a view is rendered, what is the correct way to remove it? Should all UI elements be conditionally controlled to determine whether they appear or not?
Below is an example of how I’m currently handling this, but it doesn’t feel like the right approach for dynamically removing a view at runtime.
Can someone guide me on the best way to remove views in SwiftUI?
struct ContentView: View {
@State private var isVisible = true
var body: some View {
VStack {
if isVisible { // set this to false to remove TextView?
Text("Hello, SwiftUI!")
.padding()
}
Button("Toggle View") {
...
}
}
}
}
Hello.
I have created the UIComponent using SwiftUI TextField.
struct SearchBar: View {
@Binding private var text: String
var body: some View {
HStack {
TextField("", text: $text, prompt: Text("Search"))
.textFieldStyle(.plain)
.padding()
.foregroundStyle(.white)
Button {
text = ""
} label: {
Image(systemName: "xmark")
.foregroundStyle(.black)
}
}
.padding(.horizontal, 8)
.background(RoundedRectangle(cornerRadius: 8).fill(.gray))
.padding(.horizontal, 8)
}
init(text: Binding<String>) {
_text = text
}
}
struct ParentView: View {
@State var text = ""
var body: some View {
SearchBar(text: $text)
}
}
A text of type String is binded to the component and the text is cleared when the xmark button on the right is pressed.
However, the Japanese text is not cleared under certain conditions.
Type in Hiragana, press xmark without pressing “confirm” on the keyboard → can be cleared.
Type Hiragana, press “confirm” on the keyboard, and then press xmark→Cannot be cleared.
Convert Japanese text to Kanji characters → can be cleared
Does anyone know of a workaround?
The Xcode I used is 16.0.