I honestly thought I was getting somewhere with this, but alas, no. Every time I do anything in my List of ItemRows it jumps back to the top.
Here's the setup:
DataService.swift:
final class DataService {
	static let shared = DataService()
	private init() {}
	let coreData: CoreData = CoreData()
	let modelData: ModelData = ModelData()
}
ModelData.swift:
@Observable
class ModelData: ObservableObject {
	var allItems: [ItemDetails]
	var standardItems: [ItemDetails]
	var archivedItems: [ItemDetails]
	init() {
		allItems = []
		standardItems = []
		archivedItems = []
	}
	func getInitialData() {
		// Get all items, then split them into archived and non-archived sets, because you can't use `.filter` in a view...
		allItems = dataService.coreData.getAllItems()
		standardItems.append(contentsOf: allItems.filter { !$0.archived })
		archivedItems.append(contentsOf: allItems.filter { $0.archived })
	}
}
MainApp.swift:
// Get access to the data; this singleton is a global as non-view-based functions, including the `Scene`, need to access the model data
let dataService: DataService = DataService.shared
@main
struct MainApp: App {
	// Should this be @ObservedObject or @StateObject?
	@ObservedObject private var modelData: ModelData = dataService.modelData
	// I would use @StateObject if the line was...
	//@StateObject private var modelData: ModelData = ModelData()  // right?
	// But then I couldn't use modelData outside of the view hierarchy
	var body: some Scene {
		WindowGroup {
			ZStack {
				MainView()
					.environment(modelData)
			}
		}
		.onAppear {
			modelData.getInitialData()
		}
	}
}
MainView.swift:
struct MainView: View {
	@Environment(ModelData.self) private var modelData: ModelData
	var body: some View {
		...
		ForEach(modelData.standardItems) { item in
			ItemRow(item)
		}
		ForEach(modelData.archivedItems) { item in
			ItemRow(item)
		}
	}
}
ItemRow.swift:
struct ItemRow: View {
	@Environment(\.accessibilityDifferentiateWithoutColor) private var accessibilityDifferentiateWithoutColor
	var item: ItemDetails
	@State private var showDeleteConfirmation: Bool = false
	var body: some View {
		// Construct the row view
		// `accessibilityDifferentiateWithoutColor` is used within the row to change colours if DWC is enabled, e.g. use different symbols instead of different colours for button images.
		// Add the .leftSwipeButtons, .rightSwipeButtons, and .contextMenu
		// Add the .confirmationDialog for when I want to ask for confirmation before deleting an item
	}
}
Now, the problems:
Swipe an item row, tap one of the buttons, e.g. edit, and the list refreshes and jumps back to the top. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor changed. Why did accessibilityDifferentiateWithoutColor change? The setting in Settings > Accessibility > Display & Text Size has not been changed, so why does the row's view think it changed?
With a .confirmationDialog attached to the end of the ItemRow (as seen in the code above), if I swipe and tap the delete button the list refreshes and jumps back to the top again. In the console I see: ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor, _showDeleteConfirmation changed. Right, it changed for the one row that I tapped the button for. Why does every row get redrawn?
I already had to shift from using the colorScheme environment variable to add new asset colours with light and dark variants to cover this, but you can't do that with DWC.
Honestly, managing state in SwiftUI is a nightmare. I had zero problems until iOS 26 started removing one or two rows when I scrolled, and the fix for that - using @Statebject/@ObservedObject - has introduced multiple further annoying, mind-bending problems, and necessitated massive daily refactorings. And, of course, plenty of my time islost trying to figure out where a problem is in the code because "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"...
                    
                  
                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
                    
                      This example is based on the latest version of Swift 6.2 for macOS.
I have several classes that cannot conform to Codable for various reasons. This, unfortunately, prevents me from using Transferable. If I serialize a class instance into a Data blob and use that for drag-and-drop, it works perfectly — the drag operation succeeds. However, the destination has no way to distinguish between different types of Data blobs.
All I’d need is a way to specify a unique identifier and type that I could reference in the drop handler to determine what kind of object I’m working with.
Being restricted to Transferable feels quite limiting when your data models can’t conform to Codable. It’s honestly frustrating. Has anyone else run into this issue? Is there a reliable workaround?
I tried creating a Transferable wrapper like this:
struct CustomObjectTransfer: Codable, Transferable
{
    var data: Data
    static var transferRepresentation: some TransferRepresentation
    {
// Cannot use '.myGreatSettings' because Main actor–isolated static property 'myGreatSettings' cannot be referenced from a nonisolated context       
CodableRepresentation(contentType: .init(exportedAs: "com.yourProject.settings"))
    }
}
extension UTType
{
    static let myGreatSettings: UTType = UTType("com.yourProject.settings")!
}
In my list view
.draggable ( CustomObjectTransfer(data: myObjectData) )
The UI correctly recognizes the item as draggable. If I misspell the exportedAs identifier, it even throws an error, confirming that the exported type is being recognized by the system.
However, the drop destination completely ignores this type:
.dropDestination(for: CustomObjectTransfer.self)
							{ items, location in
								dump(items)
								return true
							}isTargeted:
							{ isTargeted in
								myDestinationIsTargeted = isTargeted
							}
If I switch to using Data.self directly — wrapping the original object data manually — everything works. I can deserialize and validate the data as needed.
The problem arises when handling multiple custom drag types. The drop target accepts any data, regardless of its intended type. You only find out the actual type after the drop occurs, during validation — which is too late. By then, isTargeted has already turned true, making the drop appear valid to the user when it actually isn’t.
Again anyone else feel this pain?  Or is there a workaround that I am missing?
                    
                  
                
                    
                      We have an UIViewController called InfoPlayerViewController. Its main subview is from a child view controller backed by SwiftUI via UIHostingController. The InfoPlayerViewController conforms to UIViewControllerTransitioningDelegate. The animation controller for dismissing is DismissPlayerAnimationController. It runs UIKit keyframe animations via UIViewPropertyAnimator. When the keyframe animation is executed there’s an occasional crash for end users in production. It only happens on iOS 26.
FB Radar: FB20871547
An example crash is below.
Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Reason: +[_SwiftUILayerDelegate _screen]: unrecognized selector sent to class 0x20c95da08
Termination Reason: SIGNAL 6 Abort trap: 6
Triggered by Thread:  0
Last Exception Backtrace:
0   CoreFoundation                	0x1a23828c8 __exceptionPreprocess + 164 (NSException.m:249)
1   libobjc.A.dylib               	0x19f2f97c4 objc_exception_throw + 88 (objc-exception.mm:356)
2   CoreFoundation                	0x1a241e6cc +[NSObject(NSObject) doesNotRecognizeSelector:] + 364 (NSObject.m:158)
3   CoreFoundation                	0x1a22ff4f8 ___forwarding___ + 1472 (NSForwarding.m:3616)
4   CoreFoundation                	0x1a23073a0 _CF_forwarding_prep_0 + 96 (:-1)
5   UIKitCore                     	0x1a948e880 __35-[UIViewKeyframeAnimationState pop]_block_invoke + 300 (UIView.m:2973)
6   CoreFoundation                	0x1a22cb170 __NSDICTIONARY_IS_CALLING_OUT_TO_A_BLOCK__ + 24 (NSDictionaryHelpers.m:10)
7   CoreFoundation                	0x1a245d7cc -[__NSDictionaryM enumerateKeysAndObjectsWithOptions:usingBlock:] + 288 (NSDictionaryM.m:271)
8   UIKitCore                     	0x1a948e6bc -[UIViewKeyframeAnimationState pop] + 376 (UIView.m:2955)
9   UIKitCore                     	0x1a7bc40e8 +[UIViewAnimationState popAnimationState] + 60 (UIView.m:1250)
10  UIKitCore                     	0x1a94acc44 +[UIView(UIViewAnimationWithBlocks) _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 684 (UIView.m:17669)
11  UIKitCore                     	0x1a94ae334 +[UIView(UIViewKeyframeAnimations) animateKeyframesWithDuration:delay:options:animations:completion:] + 224 (UIView.m:17945)
12  MyApp                       	0x102c78dec static UIView.animateNestedKeyframe(withRelativeStartTime:relativeDuration:animations:) + 208 (UIView+AnimateNestedKeyframe.swift:10)
13  MyApp                       	0x102aef3c0 closure #1 in DismissPlayerAnimationController.slideDownBelowTabBarTransitionAnimator(using:) + 156 (DismissPlayerAnimationController.swift:229)
14  MyApp                       	0x102a2d3d4 <deduplicated_symbol> + 28
15  UIKitCore                     	0x1a7d5ae5c -[UIViewPropertyAnimator _runAnimations] + 172 (UIViewPropertyAnimator.m:2123)
16  UIKitCore                     	0x1a83e1594 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_3 + 92 (UIViewPropertyAnimator.m:3557)
17  UIKitCore                     	0x1a83e1464 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke + 96 (UIViewPropertyAnimator.m:3547)
18  UIKitCore                     	0x1a83e1518 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_2 + 144 (UIViewPropertyAnimator.m:3553)
19  UIKitCore                     	0x1a83e0e64 -[UIViewPropertyAnimator _setupAnimationTracking:] + 100 (UIViewPropertyAnimator.m:3510)
20  UIKitCore                     	0x1a83e1264 -[UIViewPropertyAnimator startAnimationAsPaused:] + 728 (UIViewPropertyAnimator.m:3610)
21  UIKitCore                     	0x1a83de42c -[UIViewPropertyAnimator pauseAnimation] + 68 (UIViewPropertyAnimator.m:2753)
22  UIKitCore                     	0x1a87d5328 -[UIPercentDrivenInteractiveTransition _startInterruptibleTransition:] + 244 (UIViewControllerTransitioning.m:982)
23  UIKitCore                     	0x1a87d5514 -[UIPercentDrivenInteractiveTransition startInteractiveTransition:] + 184 (UIViewControllerTransitioning.m:1012)
24  UIKitCore                     	0x1a7c7931c ___UIViewControllerTransitioningRunCustomTransitionWithRequest_block_invoke_3 + 152 (UIViewControllerTransitioning.m:1579)
25  UIKitCore                     	0x1a892aefc +[UIKeyboardSceneDelegate _pinInputViewsForKeyboardSceneDelegate:onBehalfOfResponder:duringBlock:] + 96 (UIKeyboardSceneDelegate.m:3518)
26  UIKitCore                     	0x1a7c79238 ___UIViewControllerTransitioningRunCustomTransitionWithRequest_block_invoke_2 + 236 (UIViewControllerTransitioning.m:1571)
27  UIKitCore                     	0x1a94ab4b8 +[UIView(Animation) _setAlongsideAnimations:toRunByEndOfBlock:animated:] + 188 (UIView.m:17089)
28  UIKitCore                     	0x1a7c79070 _UIViewControllerTransitioningRunCustomTransitionWithRequest + 556 (UIViewControllerTransitioning.m:1560)
29  UIKitCore                     	0x1a86cb7cc __77-[UIPresentationController runTransitionForCurrentStateAnimated:handoffData:]_block_invoke_3 + 1784 (UIPresentationController.m:1504)
30  UIKitCore                     	0x1a7c43888 -[_UIAfterCACommitBlock run] + 72 (_UIAfterCACommitQueue.m:137)
31  UIKitCore                     	0x1a7c437c0 -[_UIAfterCACommitQueue flush] + 168 (_UIAfterCACommitQueue.m:228)
32  UIKitCore                     	0x1a7c436d0 _runAfterCACommitDeferredBlocks + 260 (UIApplication.m:3297)
33  UIKitCore                     	0x1a7c43c34 _cleanUpAfterCAFlushAndRunDeferredBlocks + 80 (UIApplication.m:3275)
34  UIKitCore                     	0x1a7c1f104 _UIApplicationFlushCATransaction + 72 (UIApplication.m:3338)
35  UIKitCore                     	0x1a7c1f024 __setupUpdateSequence_block_invoke_2 + 352 (_UIUpdateScheduler.m:1634)
36  UIKitCore                     	0x1a7c2cee8 _UIUpdateSequenceRunNext + 128 (_UIUpdateSequence.mm:189)
37  UIKitCore                     	0x1a7c2c378 schedulerStepScheduledMainSectionContinue + 60 (_UIUpdateScheduler.m:1185)
38  UpdateCycle                   	0x28c58f5f8 UC::DriverCore::continueProcessing() + 84 (UCDriver.cc:288)
39  CoreFoundation                	0x1a2323230 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (CFRunLoop.c:2021)
40  CoreFoundation                	0x1a23231a4 __CFRunLoopDoSource0 + 172 (CFRunLoop.c:2065)
41  CoreFoundation                	0x1a2300c6c __CFRunLoopDoSources0 + 232 (CFRunLoop.c:2102)
42  CoreFoundation                	0x1a22d68b0 __CFRunLoopRun + 820 (CFRunLoop.c:2983)
43  CoreFoundation                	0x1a22d5c44 _CFRunLoopRunSpecificWithOptions + 532 (CFRunLoop.c:3462)
44  GraphicsServices              	0x2416a2498 GSEventRunModal + 120 (GSEvent.c:2049)
45  UIKitCore                     	0x1a7c50ddc -[UIApplication _run] + 792 (UIApplication.m:3899)
46  UIKitCore                     	0x1a7bf5b0c UIApplicationMain + 336 (UIApplication.m:5574)
// ...
                    
                  
                
                    
                      Hi, I've created an carousel using an Tabview component and it is divided in pages, each page has 7 items. Now we wan't to load the carousel page by page, but whenever I updated the list which Tabview uses to render, it flickers because it rerenders previous elements.
I've tried to use a scroll view for this but this app is for Vision Pro and I added some 3D transformations (position/rotation) to the cards from the tabview and when I added this in the scrollview they becom unclickable.
Do you have any sugestions what can I do?
Bellow is a sample of the code, I put [items] just to suggest there is a list.
VStack (spacing: 45) {
            TabView(selection: $navigationModel.carouselSelectedPage) {
                let orderedArtist = [items]
                let numberOfPages = Int((Double(orderedArtist.count) / Double(cardsPerPage)).rounded(.up))
                
                if(numberOfPages != 1){
                    Spacer().tag(-1)
                }
                
                ForEach(0..<numberOfPages, id: \.self) { page in
                    LazyHStack(alignment: .top, spacing: 16){
                        ForEach(0..<cardsPerPage, id: \.self) { index in
                            if(page * cardsPerPage + index < orderedArtist.count){
                                let item = orderedArtist[page * cardsPerPage + index]
                                GeometryReader{proxy in
                    
                  
                
                    
                      We have a custom implementation of what we call a “Scrollable Header” in our app. After building with Xcode 26, we’ve observed a change in behavior with the List component.
The issue can be seen in the attached GIF:
As the user scrolls up, the header is expected to collapse smoothly, and it does—until the moment the next list item becomes visible. At that point, the header collapses prematurely, without any apparent reason.
We’ve identified that this behavior occurs after the list’s data-fetching logic runs, which loads additional items as the user scrolls.
Below is the ViewModifier responsible for handling the collapsing header logic:
@available(iOS 18.0, *)
public struct L3CollapseHeaderIOS18: ViewModifier {
    private let minHeight: Double = 0
    private let expandedHeight: CGFloat
    private let L3Height = 44.0
    private let isViewVisible: Bool
    @Binding private var currentHeight: CGFloat
    @State private var lastOffset: ScrollOffsetInfo = ScrollOffsetInfo(offset: 0.0, offsetToBottom: 0.0, scrollableContent: 0.0)
    init(currentHeight: Binding<CGFloat>, expectedHeight: CGFloat, isViewVisible: Bool) {
        self._currentHeight = currentHeight
        self.expandedHeight = expectedHeight
        self.isViewVisible = isViewVisible
    }
    public func body(content: Content) -> some View {
        content
            .onScrollGeometryChange(for: ScrollOffsetInfo.self, of: { geometry in
                if isViewVisible {
                    return ScrollOffsetInfo(offset: geometry.contentOffset.y, offsetToBottom: (geometry.contentSize.height) - (geometry.contentOffset.y + geometry.containerSize.height), scrollableContent: geometry.contentSize.height - geometry.containerSize.height)
                } else {
                    return lastOffset
                }
            }, action: { oldValue, newValue in
                if isViewVisible {
                    expandOrCollapseHeader(oldValue: oldValue, newValue: newValue)
                }
            })
    }
    private func expandOrCollapseHeader(oldValue: ScrollOffsetInfo, newValue: ScrollOffsetInfo) {
        let oldScrollableContent = round(oldValue.scrollableContent)
        let newScrollableContent = round(newValue.scrollableContent)
        print("@@ scrollable content: \(newScrollableContent), old value: \(oldScrollableContent)")
        if newScrollableContent != oldScrollableContent {/*abs(newValue.offset) - abs(oldValue.offset) > 80*/
            return
        }
        
        let isInitialPosition = newValue.offset == 0 && lastOffset.offset == 0
        let isTryingToBounceUp = newValue.offset < 0
        let isTryingToBounceDown = newValue.offsetToBottom < 0
        // As the header collapses, the scrollable content decreases its size
        let remainingHeaderSpaceVisible = expandedHeight - currentHeight
        // remainingHeaderSpaceVisible is summed to know the exact scrollableContent size
        let isScrollableContentSmallToAnimate = (newValue.scrollableContent + remainingHeaderSpaceVisible)  < (expandedHeight * 2 + currentHeight)
        if isInitialPosition || isScrollableContentSmallToAnimate {
            expandHeader(0)
            return
        }
        let scrollDirection = scrollDirection(newValue, oldOffset: oldValue)
        switch scrollDirection {
        case .up(let value):
            if isTryingToBounceUp {
                expandHeader(0)
                return
            }
            print("@@ will collapse with value: \(value)")
            collapseHeader(value)
        case .down(let value):
            
            if isTryingToBounceDown {
                collapseHeader(0)
                return
            }
            print("@@ will expand with value: \(value)")
            expandHeader(value)
        case .insignificant:
            return
        }
    }
    private func expandHeader(_ value: CGFloat) {
        currentHeight = min(68.0, currentHeight - value)
    }
    private func collapseHeader(_ value: CGFloat) {
        currentHeight = max(0, currentHeight - value)
    }
    private func scrollDirection(_ currentOffset: ScrollOffsetInfo, oldOffset: ScrollOffsetInfo) -> ScrollDirection {
        let scrollOffsetDifference = abs(currentOffset.offset) - abs(oldOffset.offset)
        print("@@ currentOffset: \(currentOffset.offset), oldOffset: \(oldOffset.offset), difference: \(scrollOffsetDifference)")
        let status: ScrollDirection = scrollOffsetDifference > 0
        ? .up(scrollOffsetDifference)
        : .down(scrollOffsetDifference)
        
        lastOffset = currentOffset
        return status
    }
    enum ScrollDirection {
        case up(CGFloat)
        case down(CGFloat)
        case insignificant
    }
}
public struct ScrollOffsetInfo: Equatable {
    public let offset: CGFloat
    public let offsetToBottom: CGFloat
    public let scrollableContent: CGFloat
}
public struct ScrollOffsetInfoPreferenceKey: PreferenceKey {
    public static var defaultValue: ScrollOffsetInfo = ScrollOffsetInfo(offset: 0, offsetToBottom: 0, scrollableContent: 0)
    public static func reduce(value: inout ScrollOffsetInfo, nextValue: () -> ScrollOffsetInfo) {
    }
}
We use this ViewModifier to monitor updates to the List’s frame via the onScrollGeometryChange method.
From our investigation (see the screenshot below), it appears that onScrollGeometryChange is triggered just before the content size updates. The first event we receive corresponds to the view’s offset, and only nanoseconds later do we receive updates about the content’s scrollable height.
I’ll share the code for the List component using this modifier in the comments (it exceeded the character limit here).
Can anyone help us understand why this change in behavior occurs after building with Xcode 26?
Thanks in advance for any insights.
                    
                  
                
                    
                      According to the UtilityWindow documentation:
• They receive FocusedValues from the focused main scene in an application, similar to commands in the main menu, which can be used to display information on the active content as the user focuses on different scenes.
This makes me think that any UtilityWindow, even across Scenes, should always receive @FocusedValues from the currently focused window of any scene.
However, the following code doesn't quite work:
@Observable class Info: Identifiable {
    let id = UUID()
}
struct ExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView() // Inside, it uses .focusedSceneValue(info)
        }
        UtilityWindow("Test") {
            UtilityWindowContent()
        }
    }
}
struct UtilityWindowContent: View {
    @FocusedValue(Info.self) var info
    var body: some View {
        Text(info?.id ?? "<nil>") // This always shows "<nil>"
    }
}
Here's some annotated screenshots of what's happening in a project:
Menu bar commands do work with the same setup:
As a workaround, attempting to use @FocusedValue in the App struct works, but as a result, the value appears to instantiate multiple times in the background, and they re-instantiate with every focus change.
The @FocusedValue variable gets the correct instance, but it's needlessly initializing others in the background.
This is on macOS 26.0 Tahoe Beta 8 (25A5349a).
                    
                  
                
                    
                      Hi everyone!
I've encountered an issue when using Sheet + ScrollView on Mac Catalyst: the buttons in the toolbar appear with an abnormal gray color.
import SwiftUI
struct ContentView: View {
    var body: some View {
        VStack {
        }
        .sheet(isPresented: .constant(true)) {
            Sheet()
        }
    }
}
struct Sheet: View {
    var body: some View {
        NavigationStack {
            ScrollView { // <-- no issue if use List
            }
            .toolbar {
                Button(action: {}) { // <-- 👀 weird gray color
                    Image(systemName: "checkmark")
                }
            }
        }
    }
}
Steps to Reproduce:
On macOS 26.0 beta 9, use Xcode 26.0 beta 7 to create an iOS project and enable Mac Catalyst.
Paste the code above.
Select the Mac Catalyst scheme and run the project.
The buttons in the toolbar show a strange gray appearance.
If you change the ScrollView to a List in the code, the issue does not occur.
FB20120285
                    
                  
                
                    
                      According to Apple's documentation, SKOverlay is designed to recommend other applications to users. I'm seeking clarification on whether it also supports displaying update prompts for the host application itself.
Use case:
My app (for example, HelloDeveloper) is live at version 2.0, but some users are still on version 1.0. I want to display a soft update prompt that allows users to remain in the app.
Question:
Is it possible to use SKOverlay with my app's App Store ID to present an update option without requiring users to leave the app?
                    
                  
                
                    
                      If the cell is expandable in List View, it will show slowly after deletion swipe.
the image shows how it looks like when this issue happens
and it should be easy to reproduce with this sample codes
// Model for list items
struct ListItem: Identifiable {
    let id = UUID()
    let title: String
    let createdDate: Date
    let description: String
}
struct ExpandableListView: View {
    // Sample constant data
    private let items: [ListItem] = [
        ListItem(
            title: "First Item",
            createdDate: Date().addingTimeInterval(-86400 * 5),
            description: "This is a detailed description for the first item. It contains two lines of text to show when expanded."
        ),
        ListItem(
            title: "Second Item",
            createdDate: Date().addingTimeInterval(-86400 * 3),
            description: "This is a detailed description for the second item. It provides additional information about this entry."
        ),
        ListItem(
            title: "Third Item",
            createdDate: Date().addingTimeInterval(-86400 * 1),
            description: "This is a detailed description for the third item. Tap to expand and see more details here."
        ),
        ListItem(
            title: "Fourth Item",
            createdDate: Date(),
            description: "This is a detailed description for the fourth item. It shows comprehensive information when tapped."
        )
    ]
    // Track which item is currently selected/expanded
    @State private var selectedItemId: UUID? = nil
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    ExpandableCell(
                        item: item,
                        isExpanded: selectedItemId == item.id
                    ) {
                        // Handle tap - toggle selection
                        withAnimation(.easeInOut(duration: 0.3)) {
                            if selectedItemId == item.id {
                                selectedItemId = nil
                            } else {
                                selectedItemId = item.id
                            }
                        }
                    }
                }
                .onDelete { indexSet in
                    for index in indexSet {
                        print("delete \(items[index].title)")
                    }
                }
            }
            .navigationTitle("Items")
        }
    }
}
struct ExpandableCell: View {
    let item: ListItem
    let isExpanded: Bool
    let onTap: () -> Void
    private var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .short
        return formatter
    }
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            // Always visible: Title and Date
            HStack {
                VStack(alignment: .leading, spacing: 4) {
                    Text(item.title)
                        .font(.headline)
                        .foregroundColor(.primary)
                    Text(dateFormatter.string(from: item.createdDate))
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                Spacer()
                // Chevron indicator
                Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
                    .foregroundColor(.secondary)
                    .font(.caption)
            }
            // Expandable: Description (2 lines)
            if isExpanded {
                Text(item.description)
                    .font(.body)
                    .foregroundColor(.secondary)
                    .lineLimit(2)
                    .fixedSize(horizontal: false, vertical: true)
                    .transition(.opacity.combined(with: .move(edge: .top)))
                    .padding(.top, 4)
            }
        }
        .padding(.vertical, 8)
        .contentShape(Rectangle())
        .onTapGesture {
            onTap()
        }
    }
}
#Preview {
    ExpandableListView()
}
                    
                  
                
                    
                      The scroll position is not updated when orientation changes in a ScrollView with .scrollTargetBehavior(.viewAligned) and .scrollPosition().
import SwiftUI
struct ContentView: View {
    let colors: [Color] = [.red, .yellow, .cyan, .blue, .teal, .brown, .orange, .indigo]
    @State private var selected: Int? = 0
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack(spacing: 0) {
                ForEach(0..<colors.count, id: \.self) { index in
                    Rectangle()
                        .fill(colors[index])
                        .containerRelativeFrame(.horizontal)
                        .overlay {
                            Text(colors[index].description)
                        }
                }
            }
            .scrollTargetLayout()
        }
        .scrollPosition(id: $selected)
        .scrollTargetBehavior(.viewAligned)
    }
}
#Preview {
    ContentView()
}
@main
struct ViewAlignedScrollBugApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
Tested on Xcode 15.3 (15E204a), iOS 17.3.1 iPhone, iOS 17.4 Simulator.
Bug report FB13685677 filed with Apple.
                    
                  
                
                    
                      I'm building a Swift video editor with AVFoundation and a custom compositor. Despite setting AVVideoComposition.frameDuration to 60 FPS, I'm seeing significant frame skipping during playback.
Console Output Shows Frame Skipping
Frame #0 at 0.0 ms (fps: 60.0)
Frame #2 at 33.333333333333336 ms (fps: 60.0)
Frame #6 at 100.0 ms (fps: 60.0)
Frame #10 at 166.66666666666666 ms (fps: 60.0)
Frame #32 at 533.3333333333334 ms (fps: 60.0)
Frame #62 at 1033.3333333333335 ms (fps: 60.0)
Frame #96 at 1600.0 ms (fps: 60.0)
Instead of frames every ~16.67ms (60 FPS), I'm getting irregular intervals, sometimes 33ms, 67ms, or hundreds of milliseconds apart.
Renderer.swift (Key Parts)
@MainActor
class Renderer: ObservableObject {
    @Published var playerItem: AVPlayerItem?
    private let assetManager: ProjectAssetManager?
    private let compositorId: String
    
    func buildComposition() async {
        // ... load mouse moves/clicks data ...
        
        let composition = AVMutableComposition()
        let videoTrack = composition.addMutableTrack(
            withMediaType: .video,
            preferredTrackID: kCMPersistentTrackID_Invalid
        )
        
        var currentTime = CMTime.zero
        var layerInstructions: [AVMutableVideoCompositionLayerInstruction] = []
        
        // Insert video segments
        for videoURL in videoURLs {
            let asset = AVAsset(url: videoURL)
            let tracks = try await asset.loadTracks(withMediaType: .video)
            let assetVideoTrack = tracks.first
            let duration = try await asset.load(.duration)
            
            try videoTrack.insertTimeRange(
                CMTimeRange(start: .zero, duration: duration),
                of: assetVideoTrack,
                at: currentTime
            )
            
            let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
            let transform = try await assetVideoTrack.load(.preferredTransform)
            layerInstruction.setTransform(transform, at: currentTime)
            layerInstructions.append(layerInstruction)
            
            currentTime = CMTimeAdd(currentTime, duration)
        }
        
        let videoComposition = AVMutableVideoComposition()
        videoComposition.frameDuration = CMTime(value: 1, timescale: 60) // 60 FPS
        
        // Set render size from first video
        if let firstURL = videoURLs.first {
            let firstAsset = AVAsset(url: firstURL)
            let firstTrack = try await firstAsset.loadTracks(withMediaType: .video).first
            let naturalSize = try await firstTrack.load(.naturalSize)
            let transform = try await firstTrack.load(.preferredTransform)
            videoComposition.renderSize = CGSize(
                width: abs(naturalSize.applying(transform).width),
                height: abs(naturalSize.applying(transform).height)
            )
        }
        
        let instruction = CompositorInstruction()
        instruction.timeRange = CMTimeRange(start: .zero, duration: currentTime)
        instruction.layerInstructions = layerInstructions
        instruction.compositorId = compositorId
        videoComposition.instructions = [instruction]
        videoComposition.customVideoCompositorClass = CustomVideoCompositor.self
        
        let playerItem = AVPlayerItem(asset: composition)
        playerItem.videoComposition = videoComposition
        self.playerItem = playerItem
    }
}
class CompositorInstruction: NSObject, AVVideoCompositionInstructionProtocol {
    var timeRange: CMTimeRange = .zero
    var enablePostProcessing: Bool = false
    var containsTweening: Bool = false
    var requiredSourceTrackIDs: [NSValue]?
    var passthroughTrackID: CMPersistentTrackID = kCMPersistentTrackID_Invalid
    var layerInstructions: [AVVideoCompositionLayerInstruction] = []
    var compositorId: String = ""
}
class CustomVideoCompositor: NSObject, AVVideoCompositing {
    var sourcePixelBufferAttributes: [String : Any]? = [
        kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)
    ]
    
    var requiredPixelBufferAttributesForRenderContext: [String : Any] = [
        kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)
    ]
    
    func renderContextChanged(_ newRenderContext: AVVideoCompositionRenderContext) {}
    
    func startRequest(_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest) {
        guard let sourceTrackID = asyncVideoCompositionRequest.sourceTrackIDs.first?.int32Value,
              let sourcePixelBuffer = asyncVideoCompositionRequest.sourceFrame(byTrackID: sourceTrackID),
              let outputBuffer = asyncVideoCompositionRequest.renderContext.newPixelBuffer() else {
            asyncVideoCompositionRequest.finish(with: NSError(domain: "VideoCompositor", code: -1))
            return
        }
        
        let videoComposition = asyncVideoCompositionRequest.renderContext.videoComposition
        let frameDuration = videoComposition.frameDuration
        let fps = Double(frameDuration.timescale) / Double(frameDuration.value)
        let compositionTime = asyncVideoCompositionRequest.compositionTime
        let seconds = CMTimeGetSeconds(compositionTime)
        let frameInMilliseconds = seconds * 1000
        let frameNumber = Int(round(seconds * fps))
        
        print("Frame #\(frameNumber) at \(frameInMilliseconds) ms (fps: \(fps))")
        
        asyncVideoCompositionRequest.finish(withComposedVideoFrame: outputBuffer)
    }
    
    func cancelAllPendingVideoCompositionRequests() {}
}
VideoPlayerViewModel
@MainActor
class VideoPlayerViewModel: ObservableObject {
    let player = AVPlayer()
    private let renderer: Renderer
    
    func loadVideo() async {
        await renderer.buildComposition()
        if let playerItem = renderer.playerItem {
            player.replaceCurrentItem(with: playerItem)
        }
    }
}
What I've Tried
Frame skipping is consistent—exact same timestamps on every playback
Issue persists even with minimal processing (just passing through buffers)
Occurs regardless of compositor complexity
Please note that I need every frame at exact millisecond intervals for my application. Frame loss or inconsistent frameInMillisecond values are not acceptable.
                    
                  
                
                    
                      I’m seeing a layout issue in SwiftUI on iOS 26 that only reproduces with specific Accessibility Motion settings.
Steps to reproduce
1.	Open Settings → Accessibility → Motion.
2.	Enable Reduce Motion and Prefer Cross-Fade Transitions.
3.	Launch an app with a SwiftUI TextField.
4.	Tap the field to show the keyboard.
5.	Dismiss the keyboard (tap outside, swipe down, etc.).
Expected:
After the keyboard is dismissed, the view’s bottom safe area / layout should return to normal.
Actual:
The view continues to reserve space equal to the keyboard height — as if the keyboard were still visible. UI anchored to the safe area remains shifted upward until the view is reloaded.
                    
                  
                
                    
                      Hello,
I am in the process of implementing SharePlay support in my visionOS app. Everything runs fine when I test locally, but when my app is distributed via TestFlight, calling try await activity.activate() shows the SharePlay dialog as usual, but then when I start a new FaceTime call, my ImmersiveSpace gets dismissed.
This is only happening when the app is distributed via TestFlight, when I run it locally the ImmersiveSpace stays active as expected.
Looking at the console on my Mac I found this log:
Invalid initial client settings class: UIApplicationSceneClientSettings; expected class: MRUISharedApplicationSceneClientSettings; bundle ID: com.apple.facetime; scene ID: com.apple.facetime:SFBSystemService-DDA8C751-C0C4-487E-AD85-59EF4E6C6050
Does anyone have an idea how I can fix this? It's driving me nuts and I wasted over a day looking for a workaround but so far been unsuccessful.
Thanks!
                    
                  
                
                    
                      This is another issue found after changing to use a @StateObject for my data model when populating a List.
Previous issue is here: https://developer.apple.com/forums/thread/805202 - the entire List was being redrawn when one value changed, and it jumped to the top.
Here's some code:
struct ItemListView: View {
	@State private var showAlert: Bool = false
	...
	fileprivate func drawItemRow(_ item: ItemDetails) -> some View {
		return ItemRow(item: item)
			.id(item.id)
			.swipeActions(edge: .trailing, allowsFullSwipe: false) {
				RightSwipeButtons(showAlert: $showAlert, item: item)
			}
	}
	...
	List {
		ForEach(modelData.filteredItems.filter { !$0.archived }) { item in
			drawItemRow(item)
		}
	}
	...
	.alert("Delete Item"), isPresented: $showAlert) {
		Button("Yes, delete", role: .destructive) {
			deleteItem(item.id)  // Not important how this item.id is gained
		}
		Button("Cancel", role: .cancel) { }
	} message: {
		Text("Are you sure you want to delete this item? You cannot undo this.")
	}
}
struct RightSwipeButtons: View {
	@Binding var showAlert: Bool
	var body: some View {
		Button { showAlert = true } label: { Label("", systemImage: "trash") }
	}
}
The issue I have now is that when you swipe from the right to show the Delete button, and tap it, the alert is displayed but the list has jumped back to the top again. At this point you haven't pressed the delete button on the alert.
Using let _ = Self._printChanges() on both the ItemsListView and the individual ItemRows shows this:
ItemsListView: _showAlert changed.
ItemRow: @self, @identity, _accessibilityDifferentiateWithoutColor changed.
So yeah, that's correct, showAlert did change in ItemsListView, but why does the entire view get redrawn again, and fire me back to the top of the list?
You'll notice that it also says _accessibilityDifferentiateWithoutColor changed on the ItemRows, so I commented out their use to see if they were causing the issue, and... no.
Any ideas?
(Or can someone provide a working example of how to ditch SwiftUI's List and go back to a UITableView...?)
                    
                  
                
                    
                      The keyboard looks dimmed/disabled on every keyboard and every keyboard type inside my app, but is still fully functional. The dimmed/disabled effect includes keyboards shown inside the WebView.
The bug only appears in iOS26 and iOS26.0.1, but not on any previous versions of iOS.
                    
                  
                
                    
                      I’m seeing unexpected scroll behavior when embedding a LazyVStack with dynamically sized views inside a ScrollView.
Everything works fine when the item height is fixed (e.g. colored squares), but when I switch to text views with variable height, the scroll position jumps and glitches—especially when the keyboard appears or disappears. This only happens on iOS 26, it works fine on iOS 18.
Working version
struct Model: Identifiable {
    let id = UUID()
}
struct ModernScrollView: View {
    
    @State private var models: [Model] = []
    @State private var scrollPositionID: String?
    @State private var text: String = ""
    @FocusState private var isFocused
    
    // MARK: - View
    
    var body: some View {
        scrollView
            .safeAreaInset(edge: .bottom) { controls }
            .task { reset() }
    }
    
    // MARK: - Subviews
    
    private var scrollView: some View {
        ScrollView {
            LazyVStack {
                ForEach(models) { model in
                    SquareView(color: Color(from: model.id))
                        .id(model.id.uuidString)
                }
            }
            .scrollTargetLayout()
        }
        .scrollPosition(id: $scrollPositionID)
        .scrollDismissesKeyboard(.interactively)
        .defaultScrollAnchor(.bottom)
        .onTapGesture {
            isFocused = false
        }
    }
    
    private var controls: some View {
        VStack {
            HStack {
                Button("Add to top") {
                    models.insert(contentsOf: makeModels(3), at: 0)
                }
                Button("Add to bottom") {
                    models.append(contentsOf: makeModels(3))
                }
                Button("Reset") {
                    reset()
                }
            }
            HStack {
                Button {
                    scrollPositionID = models.first?.id.uuidString
                } label: {
                    Image(systemName: "arrow.up")
                }
                Button {
                    scrollPositionID = models.last?.id.uuidString
                } label: {
                    Image(systemName: "arrow.down")
                }
            }
            TextField("Input", text: $text)
                .padding()
                .background(.ultraThinMaterial, in: .capsule)
                .focused($isFocused)
                .padding(.horizontal)
        }
        .padding(.vertical)
        .buttonStyle(.bordered)
        .background(.regularMaterial)
    }
    
    // MARK: - Private
    
    private func makeModels(_ count: Int) -> [Model] {
        (0..<count).map { _ in Model() }
    }
    
    private func reset() {
        models = makeModels(3)
    }
    
}
// MARK: - Color+UUID
private extension Color {
    init(from uuid: UUID) {
        let hash = uuid.uuidString.hashValue
        let r = Double((hash & 0xFF0000) >> 16) / 255.0
        let g = Double((hash & 0x00FF00) >> 8) / 255.0
        let b = Double(hash & 0x0000FF) / 255.0
        self.init(red: abs(r), green: abs(g), blue: abs(b))
    }
}
Not working version
When I replace the square view with a text view that generates random multiline text:
struct Model: Identifiable {
    let id = UUID()
    let text = generateRandomText(range: 1...5)
    
    // MARK: - Utils
    
    private static func generateRandomText(range: ClosedRange<Int>) -> String {
        var result = ""
        for _ in 0..<Int.random(in: range) {
            if let sentence = sentences.randomElement() {
                result += sentence
            }
        }
        return result.trimmingCharacters(in: .whitespaces)
    }
    
    private static let sentences = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
        "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
        "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    ]
}
and use it like this:
ForEach(models) { model in
    Text(model.text)
         .padding()
         .multilineTextAlignment(.leading)
         .background(Color(from: model.id))
         .id(model.id.uuidString)
}
Then on iOS 26, opening the keyboard makes the scroll position jump unpredictably.
It is more visible if you play with the app, but I could not upload a video here.
Environment
Xcode 26.0.1 - Simulators and devices on iOS 26.0 - 18.0
Questions
Is there any known change in ScrollView / scrollPosition(id:) behavior on iOS 26 related to dynamic height content?
Am I missing something in the layout setup that makes this layout unstable with variable-height cells?
Is there a workaround or recommended approach for keeping scroll position stable when keyboard appears?
                    
                  
                
                    
                      I'm trying to create a UI with two button bars (top and bottom) inside the detail view of a NavigationSplitView, instead of using the built-in .toolbar() modifier. I'm using .ignoresSafeArea(.container, edges: .vertical) so the detail view can reach into that area.
However, in macOS and iOS 26 the top button is not clickable/tappable because it is behind an invisible View created by the non-existent toolbar. Interestingly enough, if I apply .buttonStyle(.borderless) to the top button it becomes clickable (in macOS).
On the iPad the behavior is different depending on the iPad OS version. In iOS 26, the button is tappable only by the bottom half. In iOS 18 the button is always tappable.
Here's the code for the screenshot:
import SwiftUI
struct ContentView2: View {
    @State private var sidebarSelection: String?
    @State private var contentSelection: String?
    @State private var showContentColumn = true
    @State private var showBars = true
    
    var body: some View {
        NavigationSplitView {
            // Sidebar
            List(selection: $sidebarSelection) {
                Text("Show Content Column").tag("three")
                Text("Hide Content Column").tag("two")
            }
            .navigationTitle("Sidebar")
        } detail: {
            VStack(spacing: 0) {
                if showBars {
                    HStack {
                        Button("Click Me") {
                            withAnimation {
                                showBars.toggle()
                            }
                        }
                        .buttonStyle(.borderedProminent)
                    }
                    .frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50)
                    .background(.gray)
                    .transition(.move(edge: .top))
                }
                ZStack {
                    Text("Detail View")
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .init(horizontal: .center, vertical: .center))
                .border(.red)
                .onTapGesture(count: 2) {
                    withAnimation {
                        showBars.toggle()
                    }
                }
                if showBars {
                    HStack {
                        Button("Click Me") {
                            withAnimation {
                                showBars.toggle()
                            }
                        }
                    }
                    .frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50)
                    .background(.gray)
                    .transition(.move(edge: .bottom))
                }
            }
            .ignoresSafeArea(.container, edges: .vertical)
            .toolbarVisibility(.hidden)
        }
        .toolbarVisibility(.visible)
    }
}
I'm confused by this very inconsistent behavior and I haven't been able to find a way to get this UI to work across both platforms.
Does anybody know how to remove the transparent toolbar that is preventing clicks/taps in this top section of the view? I'm hoping there's an idiomatic, native SwiftUI way to do it.
                    
                  
                
                    
                      When using a TextField with axis to set to .vertical on iOS, it sets a bound selection parameter to an erroneous value. Whilst on MacOS it performs as expected.
Take the following code:
import SwiftUI
@main
struct SelectionTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
struct ContentView: View {
	@FocusState private var isFocused: Bool
	@State private var text = ""
	@State private var textSelection: TextSelection? = nil
	
    var body: some View {
		TextField("Label", text: $text, selection: $textSelection, axis: .vertical)
		.onChange(of: textSelection, initial: true) {
			if let textSelection {
				print("textSelection = \(textSelection)")
			} else {
				print("textSelection = nil")
			}
		}
		.focused($isFocused)
		.task {
			isFocused = true
		}
    }
}
Running this on MacOS target gives the following on the console:
textSelection = nil
textSelection = TextSelection(indices: SwiftUI.TextSelection.Indices.selection(Range(0[any]..<0[any])), affinity: SwiftUI.TextSelectionAffinity.downstream)
Running the code on iOS gives:
textSelection = TextSelection(indices: SwiftUI.TextSelection.Indices.selection(Range(1[any]..<1[any])), affinity: SwiftUI.TextSelectionAffinity.upstream)
textSelection = TextSelection(indices: SwiftUI.TextSelection.Indices.selection(Range(1[any]..<1[any])), affinity: SwiftUI.TextSelectionAffinity.upstream)
Note here the range is 1..<1 - which is incorrect.
Also of side interest this behaviour changes if you remove the axis parameter:
textSelection = nil
Am I missing something, or is this a bug?
                    
                  
                
                    
                      Hello,
I am writing an audio utility, with a typical audio track player, in SwiftUI for macos 26.
My current problem is that the SwiftUI stops rendering the main window UI when the window loses focus.
This is a problem since even clicking on the app menu bar has the window loose focus, and the timer, time cursor and all animations of the audio piece stop.
All baground services, audio, timers and model continue running (even tho with some crackling on the switch). Once the window focus is re-obtained the animations continue and skip to current state.
I have read that SwiftUI optimizes macos like ios, and disables the ui run loop, but there must be a way to disable, since this obviously not the case for most mac app.
Is there a solution with either SwiftUI or involving AppKit?
                    
                  
                
                    
                      In visionOS, is there a way to temporarily hide the window close/position handle at the bottom of a window?
The Safari app does this, so it must be possible.