Hello, and an early "Merry Christmas" to all,
I'm building a SwiftUI app, and one of my Views is a fullscreen UIViewRepresentable (SpriteView) beneath a SwiftUI interface.
Whenever the user interacts with any SwiftUI element, the UIView registers a hit in touchesBegan(). For example, my UIView has logic for pinching (not implemented via UIGestureRecognizer), so whenever the user holds down a SwiftUI element while touching the UIView, that counts as two touches to the UIView which invokes the pinching logic.
Things I've tried to block SwiftUI from passing the gesture down to the UIView:
Adding opaque elements beneath control elements
Adding gestures to the elements above
Adding gesture masks to the gestures above
Converting eligible elements to Buttons (since those seem immune)
Adding SpriteViews beneath those elements to absorb gestures
So far nothing has worked. As long as the UIView is beneath SwiftUI elements, any interactions with those elements will be registered as a hit.
The obvious solution is to track each SwiftUI element's size and coordinates with respect to the UIView's coordinate space, then use exclusion areas, but this is both a pain and expensive, and I find it hard to believe this is the best fix for such a seemingly basic problem.
I'm probably overlooking something basic, so any suggestions will be greatly appreciated
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
Hello everyone,
I've been having a bit of trouble with the .symbolRenderingMode(_:) modifier.
When trying to apply it to a single button in a toolbar, it does not work at all. The symbol is always rendered as monochrome.
However, I've realised that with this little hack I can achieve the expected results, but this is not ideal.
.toolbar {
HStack {
Button("", action: {}) // The hack
Button("Button", systemImage: "line.3.horizontal.decrease.circle.fill", action: {})
.symbolRenderingMode(.hierarchical)
.foregroundStyle(.blue)
}
}
I've submitted a bug report (FB16129223) but in the meantime, is this my only solution ?
Side note: the foregroundStyle(_:) modifier is ignored as well.
Hello. I am developing an application using Swift 6 and SwiftUI.
I have custom implemented a BottomSheet that animates from bottom to top, and I attempted to achieve this animation by changing the alignmentGuide like this.
ZStack(alignment: .bottom) {
dimView
.opacity(isVisible ? 1 : 0)
.transaction { transaction in
transaction.animation = .easeInOut(duration: 0.35)
}
bottomSheetView
.alignmentGuide(VerticalAlignment.bottom) { dimension in
// compile error occur because isVisible property is state of MainActor isolated View!
isVisible ? dimension[.bottom] : dimension[.top]
}
}
There were no issues in Swift 5, but now I am encountering compile errors because the computeValue closure of the alignmentGuide is not isolated to the MainActor, preventing me from calling view state values or functions.
So I am curious if there are any plans to isolate this closure to the MainActor. From my observation, this closure is always called on the main thread.
Thank you.
It happens on a brand new SwiftUI Xcode project with nothing in it but the default base code. When I build and run, I get "Error creating the CFMessagePort needed to communicate with PPT." After some Googling, I know it has something to do with Performance Tools. I don't know what's causing it.
I'm running 16.2 on a brand new M4 Mac mini.
I have an issue where a very specific configuration of .overlay, withAnimation, and a bindable state can freeze the app when the state changes.
I've isolated the problematic source code into a sample project can be found here that demonstrates the issue:
https://github.com/katagaki/IcyOverlay
Steps to Reproduce
To reproduce the issue, tap the 'Simulate Content Load' button.
Once the progress bar has completed, a switch is toggled to hide the progress view, which causes the overlay to disappear, and the app to freeze.
Any help and/or advice will be appreciated!
Development Environment
Xcode Version 16.2 (16C5032a), macOS 15.2(24C101)
iOS SDK: 18.2 (22C146), Simulator: 18.2 (22C150)
NavigationTitle does not change when the app language changes. It works well in iOS 17.5 but does not in iOS 18.x
Hi, I am having some troubles creating a "nested" RealityView content using MapKit attachment.
I am building a visionOS app that has horizontal MapKit map as an attachment to RealityView. I want to display 3D pins on that map, therefore I am using native map annotation and inside of these annotations, I create a new RealityView just for the 3D pin. This worked completely fine, unitil I wanted to have those RealityViews interact with each other.
By interaction of those RealityViews I mean that I wanted to group entities from the first "main" RealityViews content with the 3D pins using ModelSortGroupComponent.
Why I want this? I want to make the map circular, that is not a problem. Problem is that when I move the map with 3D pins, these pins have their own RealityView space and are only bounded by volumetric window dimensions. What happes is that these pins float next to the map (shown on attached image). So I came up with this solution: create a custom "toroid" like 3D entity model that occludes the pins that go outside the map region. In order to occlude only the pins, I need to use ModelSortGroupComponent to group the "toroid" entity with 3D pins entities (as described in another forum thread).
To summarize: need the content of the superior RealityView to interact with map attachment annotations RealityView content in order to group them. There might be of course another, better way to achieve my whole goal, so I would naturally appreciate any help or guidance.
Image below showing 3D pins on circular map. Since pins RealityView does no know anything about other RealityViews, it just overlows and hangs in space until is cropped by volumetric window boundary.
Simplified code:
var body: some View {
let modelSortGroup = ModelSortGroup(depthPass: .prePass)
RealityView { content, attachments in
var mainEntity = Entity()
// My other entities here...
if let mapAttachment = attachments.entity(for: "mapAttachment") {
// Edit map properties, position, horizontal layout etc.
mainEntity.addChild(mapAttachment)
}
// Create and add to content mask "toroid" entity mapMaskEntity. Use OcclusionMaterial() material.
mapMaskEntity.components.set(ModelSortGroupComponent(group: modelSortGroup, order: 0))
// For all pins, somehow also set the group
// 3DPinEntity.components.set(ModelSortGroupComponent(group: modelSortGroup, order: 1))
content.add(mainEntity)
} attachments: {
Attachment(id: "mapAttachment") {
Map {
ForEach(mapViewModel.clusters, id: \.id) { cluster in
Annotation("", coordinate: cluster.coordinate) {
MapPin3DView(cluster: cluster)
}
}
}
.clipShape(Circle())
}
}
}
// MapPin3DView is an map annotation view that includes a model of 3D pin and some details like image etc., uses RealityView.
struct MapPin3DView: View {
var body: some View {
RealityView { content in
// 3D pin entities...
}
}
}
Hello!
Is there a way to perform an action before alert (no matter which) appears and sheet (no matter which) opens? Perhaps, system event or something like this.
I have an app that I would like to implement sharing an Image from a Button within a .contextMenu as below. I’d like to share from the button in the same way as a ShareLink would.
I see other resources suggest the use of the UIActivityViewController although I was wondering if there is a SwiftUI approach that would not involve UIKit.
.contextMenu {
Button(action: {
debugPrint("Share")
}) {
Label("Share", systemImage: "square.and.arrow.up")
}
}
I want to get the availableInputs list in widgetextension, but it always returns only the phone's microphone. I have confirmed that I have connected additional Bluetooth devices. Why is this?
If I want to get the information I want, how should I do it?
We've seen an issue when using a LazyVGrid inside a List. The app crashes with:
Thread 1: Fatal error: <UpdateCoalescingCollectionView 0x600000ca0d20> is stuck in a recursive layout loop
When debugging the issue, we were able to narrow down the issue to a minimum reproducible example below:
struct ContentView: View {
let columns = [
GridItem(.adaptive(minimum: 43))
]
var body: some View {
List {
LazyVGrid(columns: columns) {
ForEach(0..<15) { value in
if value == 0 {
Text("a")
} else {
Color.clear
}
}
}
}
}
}
The issue can be reproduced on iPhone 15 Pro Max and iOS 18.x specifically.
In a production app we have a similar layout, but instead of GridItem(.adaptive) we use GridItem(.flexible).
Hi! I'm attempting to run the Quakes Sample App^1 from macOS. I am running breakpoints and confirming the mapCameraKeyframeAnimator is being called:
.mapCameraKeyframeAnimator(trigger: selectedId) { initialCamera in
let start = initialCamera.centerCoordinate
let end = quakes[selectedId]?.location.coordinate ?? start
let travelDistance = start.distance(to: end)
let duration = max(min(travelDistance / 30, 5), 1)
let finalAltitude = travelDistance > 20 ? 3_000_000 : min(initialCamera.distance, 3_000_000)
let middleAltitude = finalAltitude * max(min(travelDistance / 5, 1.5), 1)
KeyframeTrack(\MapCamera.centerCoordinate) {
CubicKeyframe(end, duration: duration)
}
KeyframeTrack(\MapCamera.distance) {
CubicKeyframe(middleAltitude, duration: duration / 2)
CubicKeyframe(finalAltitude, duration: duration / 2)
}
}
But I don't actually see any map animations taking place when that selection changes.
Running the application from iPhone simulator does show the animations.
I am building from Xcode Version 16.2 and macOS 15.2. Are there known issues with this API on macOS?
On a ScrollView+LazyVStack, the addition of .scrollTargetLayout causes many list items to be initialized, instead of the ordinary economical behavior of LazyVStack, where only the necessary items and views are initialized. Even worse, as the stack is scrolled down, all list items are reinitialized for every small scroll.
Without, .scrollTargetLayout, everything works fine.
I've tried every variation of locating modifiers, and different ways of identifying the list items, with no success.
Any ideas? Thanks.
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Post.created, ascending: true)],
animation: .default)
private var posts: FetchedResults<Post>
var body: some View {
ZStack{
ScrollView{
LazyVStack(spacing:0) {
ForEach(posts, id: \.self) { post in
PostView(post: post)
}
.onDelete(perform: deletePosts)
}.scrollTargetLayout() // <---- causes multiple Posts re-instantiations for any small scroll, very slow
}
.scrollPosition(id: $scrolledID)
.scrollTargetBehavior(.paging)
Hello!
I want to programmatically set the editMode in SwiftUI for a view as soon as it appears. However, I have read a lot now but it only works via the EditButton - but not programmatically. I'm using the simulator for this example and Xcode Version 15.4.
struct EditTodos: View {
@State private var editMode = EditMode.inactive
@State var content = ["apple", "banana", "peanut"]
var body: some View {
NavigationStack {
List {
ForEach(content, id: \.self) { item in
Text(item)
}
.onDelete { _ in }
.onMove { _, _ in }
}
.onAppear {
editMode = .active // note: not working, why??
}
.navigationBarItems(
trailing: HStack(spacing: 20) {
EditButton()
}
)
}
}
}
#Preview {
EditTodos()
}
May I inquire about the differences between the two ways of view under the hood in SwiftUI?
class MyViewModel: ObservableObject {
@Published var state: Any
init(state: Any) {
self.state = state
}
}
struct MyView: View {
@StateObject var viewModel: MyViewModel
var body: some View {
// ...
}
}
struct CustomView: View {
let navigationPath: NavigationPath
@StateObject var viewModel: MyViewModel
var body: some View {
Button("Go to My View") {
navigationPath.append(makeMyView())
}
}
}
// Option 1: A viewModel is initialized outside view's initialization
func makeMyView(state: Any) -> some View {
let viewModel = MyViewModel(state: state)
MyView(viewModel: viewModel)
}
// Option 2: A viewModel is initialized inside view's initialization
func makeMyView(state: Any) -> some View {
MyView(viewModel: MyViewModel(state: state))
}
For option 1, the view model will be initialized whenever custom view is re-rendered by changes whereas the view model is only initialized once when the view is re-rendered for option 2.
So what happens here?
This is my code in ContentView:
import SwiftUI
import SceneKit
import PlaygroundSupport
struct ContentView: View {
var body: some View {
VStack {
Text("SceneKit with SwiftUI")
.font(.headline)
.padding()
SceneView(
scene: loadScene(),
options: [.autoenablesDefaultLighting, .allowsCameraControl]
)
.frame(width: 400, height: 400)
.border(Color.gray, width: 1)
}
}
}
func loadScene() -> SCNScene? {
if let fileURL = Bundle.main.url(forResource: "a", withExtension: "dae") {
do {
let scene = try SCNScene(url: fileURL, options: [
SCNSceneSource.LoadingOption.checkConsistency: true
])
print("Scene loaded successfully.")
return scene
} catch {
print("Error loading scene: \(error.localizedDescription)")
}
} else {
print("Error: Unable to locate a.dae in Resources.")
}
return nil
}
a.dae file exists in the Resources section of macOS Playground app. And a.dae can be viewed in Xcode.
Console shows: Error loading scene: The operation couldn’t be completed. (Foundation._GenericObjCError error 0.)
Any input is appreciated.
I'm adding Admob ads to my app, and Admob needs to know the width of the view, so I'm using GeometryReader for that. To prevent GeometryReader from grabbing screen space, I've wrapped the main view in GeometryReader { }. I then use geometry.size.width in my call to the adView.
This all works fine. I have two main screens where I show ads, and they both work, until I rotate the device. Then the app crashes!
If I comment out the GeometryReader code and pass a fixed value to the ad view, I can rotate the device with no fear of a crash.
My question is: Do I have to accept that GeometryReader will crash the app when it's rotated, or is there another, stable way to get view dimensions?
Recently I noticed how my ViewModels aren't deallocating and they end up as a memory leaks. I found something similar in this thread but this is also happening without using @Observation. Check the source code below:
class CellViewModel: Identifiable {
let id = UUID()
var color: Color = Color.red
init() { print("init") }
deinit { print("deinit") }
}
struct CellView: View {
let viewModel: CellViewModel
var body: some View {
ZStack {
Color(viewModel.color)
Text(viewModel.id.uuidString)
}
}
}
@main
struct LeakApp: App {
@State var list = [CellViewModel]()
var body: some Scene {
WindowGroup {
Button("Add") {
list.append(CellViewModel())
}
Button("Remove") {
list = list.dropLast()
}
ScrollView {
LazyVStack {
ForEach(list) { model in
CellView(viewModel: model)
}
}
}
}
}
}
When I tap the Add button twice in the console I will see "init" message twice. So far so good. But then I click the Remove button twice and I don't see any "deinit" messages.
I used the Debug Memory Graph in Xcode and it showed me that two CellViewModel objects are in the memory and they are owned by the CellView and some other objects that I don't know where are they coming from (I assume from SwiftUI internally).
I tried using VStack instead of LazyVStack and that did worked a bit better but still not 100% "deinits" were in the Console.
I tried using weak var
struct CellView: View {
weak var viewModel: CellViewModel?
....
}
but this also helped only partially.
The only way to fully fix this is to have a separate class that holds the list of items and to use weak var viewModel: CellViewModel?. Something like this:
class CellViewModel: Identifiable {
let id = UUID()
var color: Color = Color.red
init() { print("init") }
deinit { print("deinit") }
}
struct CellView: View {
var viewModel: CellViewModel?
var body: some View {
ZStack {
if let viewModel = viewModel {
Color(viewModel.color)
Text(viewModel.id.uuidString)
}
}
}
}
@Observable
class ListViewModel {
var list = [CellViewModel]()
func insert() {
list.append(CellViewModel())
}
func drop() {
list = list.dropLast()
}
}
@main
struct LeakApp: App {
@State var viewModel = ListViewModel()
var body: some Scene {
WindowGroup {
Button("Add") {
viewModel.insert()
}
Button("Remove") {
viewModel.drop()
}
ScrollView {
LazyVStack {
ForEach(viewModel.list) { model in
CellView(viewModel: model)
}
}
}
}
}
}
But this won't work if I want to use @Bindable such as
@Bindable var viewModel: CellViewModel?
I don't understand why SwiftUI doesn't want to release the objects?
I recently started exploring the latest version of SwiftUI and encountered an issue while working with TabView and NavigationStack. I downloaded the example code provided by Apple and began making changes to explore new SwiftUI features. However, I noticed that the navigation bar "jumps" or resets when switching between tabs, even in their example implementation.
Here are some changes which I made below in the files:
LibraryView:
.navigationTitle("Library")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(Color("AccentColor"),for: .navigationBar)
WatchNowView:
.navigationTitle("Watch Now")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(Color("AccentColor"),for: .navigationBar)
example code url :- destinationVideo
I suspect the issue arises because each tab bar item has its own NavigationStack. When we set a navigation title for each view, the NavigationStack resets the navigation bar on view appearance, which causes this visual bug.
Thank you!
I recently started exploring the latest version of SwiftUI and came across a issue while working with TabView and NavigationStack. I downloaded Apple’s provided example code and began making changes to explore new SwiftUI changes. i added navigationtitle and toolbarBackground to first two tab. However, I noticed that the navigation bar "jumps" or resets when switching between tabs, even in their own example implementation.
Here’s a simplified version of the example code I was testing:
file name - WatchNowView
.navigationTitle("Watch Now")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(Color("AccentColor"),for: .navigationBar)
file name - LibraryView
.navigationTitle("Library")
.navigationBarTitleDisplayMode(.inline)
.toolbarBackground(Color("AccentColor"),for: .navigationBar)
Here is a sample code link (provided by Apple developer document) : destination-video
I have attached a gif below demonstrating this issue:
Questions:
Is this behavior expected in the latest version of SwiftUI, or is it a bug in the framework's handling of TabView and NavigationStack?
Is this behavior expected as all tabbar item have their own nativationStack?
Are there any official recommendations for maintaining seamless navigation experiences when using navigationStack and TabView?
This behavior detracts from the otherwise smooth experience SwiftUI aims to provide. If anyone has encountered this issue and found a workaround, I’d greatly appreciate your insights. I hope Apple can review this problem to enhance the usability of SwiftUI. Thank you!