Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Activity

[tvOS] ScrollView with Text does not scroll
I'm trying to do something so seemingly basic, yet I can't get it to work and I'm flummoxed. In a basic, vanilla SwiftUI app for tvOS, embed a single Text element with a very long string (hundreds of lines) in it: struct ContentView: View { var body: some View { ScrollView(.vertical) { Text(veryLargeString) .focusable() } } } Then fire up the app on tvOS, and it will not scroll. No matter what I do. Pressing arrow keys, swiping fast with my thumb, and nothing. It will not move. Ironically, in the Xcode SwiftUI Preview window—it does scroll, so that's always a fun tease. What I do know is that the focus engine is throwing a few errors, so it's leading me to believe the issue is with how I have the focusable element attached. I'm using a combination of -UIFocusLoggingEnabled YES as well as listening for UIFocusSystem.movementDidFailNotification. Unfortunately since this is SwiftUI, the notification failure and debugging logs aren't really all that actionable. Help appreciated!
4
4
1.8k
4w
External Display stops working at iOS 27 / Xcode 27
I have an app that connects to an external display, the app controls content for the external display, but the external display is not active and uses the UIWindowSceneSessionRoleExternalDisplayNonInteractive role. I migrated the app to use UIScene lifecycle last year and everything seemed to be working. I use - (UISceneConfiguration *) application:(UIApplication *) application configurationForConnectingSceneSession:(UISceneSession *) connectingSceneSession options:(UISceneConnectionOptions *) options; in the AppDelegate to provide the configurations. But now at XCode 27/ iOS 27, that code is not called when I connect the external display with a dislpay adapter to my iPhone. But the device connects and shows a mirror of my iPhone display. What's going on?
Topic: UI Frameworks SubTopic: UIKit
2
0
174
4w
Save & Edit UIBarButtonSystemItems need to be updated for the iPhone Duo
When creating a UIBarButtonItem using the standard UIBarButtonSystemItem values of UIBarButtonSystemItemSave or UIBarButtonSystemEdit you end up with text-only buttons. The text-only buttons are an issue with the iPhone Duo since such buttons do not get put in the vertical sidebar. This is a problem with a view controller with scrollable content. The text-only Save or Edit buttons can scroll out of view leaving the user confused about how to complete their work on the screen. Related is the UIViewController editButtonItem which gives the standard Edit/Done toggle button. Since the button shows the text "Edit" the button doesn't get put in the vertical sidebar. This is despite the Done display being shown as a checkmark icon.
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
457
4w
Best practice Replacement for Edit Button on iPhone Duo
On the iPhone Duo the standard "Edit" button for navigation bars is a text-based button, and text-based buttons are not pushed to the vertical toolbars on the Duo. This looks sometimes silly, if it is the only remaining button that can not be placed in the vertical toolbar and which has the result that an almost empty horizontal navigation bar must be kept visible as well. So I guess it would be good idea to have an icon-based replacement for the Editor button to avoid having two toolbars on the Duo. Is there a best practice for the icon to pick for such an edit button? Would be "square.and.pencil" (SF Symbols) a good choice? Or better the "pencil .circle", because the "square" one is often used for text-based input? Anything better?
4
0
1.3k
4w
Siri AI AppIntent snippets don’t size correctly and take up the full width in iOS 27.1 RC
I have been trying to create snippet views for my AppShortcuts which fill the full width of the container. Doing this via either .frame(minWidth: 0, maxWidth. infinity) or .containerRelativeFrame() work perfectly fine when my snippet is presented from Shortcuts.app (though sometimes the wrong color scheme is displayed), but when I invoke my AppShortcut from Siri AI and it presents the snippet there, it's offset on the trailing side by sometimes up to over 30 points, which has frustrating effects on the UI I am trying to implement. I've noted the insets are different too depending on whether the snippet is presented from what AppIntents method invokes it requestConfirmation or .result respectively. Here's some screenshots showing the issue. All these use the exact same snippet view, but all have different results in how they look and are inset. In App (how I want it to look) In Shortcuts (fine I guess) And finally, in the Siri app. I have reproduced this is issue and distilled it in a sample project. Please ensure this routes to the correct team: FB25164368. Thanks!
0
0
225
4w
UINavigationController, Nav Bar buttons incorrect layout on iPhone Duo
When presenting a UINavigationController presented via UIViewControllerRepresentable in a sheet, the top nav bar buttons aren't correctly moving to the side bar on the front screen of the Duo. They do re-orient vertically, but they do so next to the side bar and not within it. What am I doing wrong? This is how I am setting the buttons: let trimButton = UIBarButtonItem(image: UIImage(systemName: "selection.pin.in.out"), style: .plain, target: self, action: #selector(enterTrimming(_:))) navigationItem.leftBarButtonItem = trimButton navigationItem.rightBarButtonItem = cancelButton navigationItem.leftBarButtonItem?.isEnabled = true navigationItem.rightBarButtonItem?.isEnabled = true
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
211
4w
Vision 26.7
Apple's latest Vision 26.7, no longer recognizes Thai language( I haven't checked others), and crashes if request.recognitionLevel = .accurate Any ideas what is going on here, or just Apple's poor thoroughness at releasing an osx prematurely, as usual? let request = VNRecognizeTextRequest() request.recognitionLanguages = [language] if ProcessInfo.processInfo.isiOSAppOnMac { request.recognitionLevel = .fast // workaround for macOS 26.7.1 crash } else { request.recognitionLevel = .accurate } request.minimumTextHeight = 0
1
0
244
4w
How to automatically size a table's footer view
Hi, I would like to automatically vertically size the table's footer view so that it has a fixed pre-defined minimum height and maximum height that fills up the gap between the last cell and the bottom of the safe area. How can I do this? PS: It seems to be that the footer view's size is not included in the table view's content size, correct? Regards, Hardy
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
152
4w
iPhone Duo: navigation bar item briefly moves to vertical bar during fullScreen dismissal despite .disabled
On the folded iPhone Duo display, a navigation bar item briefly moves into the vertical bar during dismissal of a full-screen modal, then jumps back to the top. Both navigation controllers consistently return .disabled from preferredVerticalBarBehavior, with childForPreferredVerticalBarBehavior returning nil. Environment Xcode 27.1 RC (27A9275), iOS 27.1 SDK, iPhone Duo simulator, folded outer display in portrait. Steps to reproduce Run the single-file UIKit example below in a scene-based app with a generated launch screen and no main storyboard. Tap the gear button on the Home screen to present Settings using .fullScreen. Tap the close button on Settings. Watch the Home screen's gear button during the dismissal animation. Expected The gear button stays in the top horizontal navigation bar throughout the transition, because the vertical bar preference is .disabled on both navigation controllers. Actual For part of the dismissal, the gear button appears below the status icons in the right-hand vertical bar region. It then jumps back to the top horizontal bar. The underlying tab bar also temporarily moves from the bottom to the right before returning. This reproduces with an otherwise empty view, standard UIKit containers, and the default bar appearance. No app code changes safeAreaInsets, bar frames, or animation timing. During the presentation/dismissal cycle, the Home view's logged right safe-area inset changes from 0 to 88 points and back to 0. In our app, content constrained to the safe area visibly narrows during the same transient change. In separate comparisons, returning .disabled from the root tab controller and content controllers, or requesting setNeedsUpdateOfVerticalBarConfiguration in appearance callbacks, did not eliminate the issue. Changing the modal to .overFullScreen avoided it in the tested simulator, but changes the underlying view controller lifecycle. The example below intentionally keeps .fullScreen to reproduce the problem. Is this expected behavior, or a known UIKit issue? Is there a supported way to keep .disabled effective throughout a fullScreen presentation/dismissal? Has this been addressed in a newer SDK/runtime? I have not tested iOS 27.2 or a physical Duo device. Minimal reproduction (one Swift file): import UIKit @main final class DemoAppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let configuration = UISceneConfiguration(name: "Demo", sessionRole: session.role) configuration.delegateClass = DemoSceneDelegate.self return configuration } } final class DemoSceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options: UIScene.ConnectionOptions) { guard let scene = scene as? UIWindowScene else { return } let home = DemoController() home.title = "首页" let navigation = DemoNavigationController(rootViewController: home) navigation.tabBarItem = UITabBarItem(title: "首页", image: UIImage(systemName: "house"), selectedImage: nil) let tabs = UITabBarController() if #available(iOS 18.0, *) { tabs.mode = .tabBar } tabs.viewControllers = [navigation] let window = UIWindow(windowScene: scene) window.rootViewController = tabs self.window = window window.makeKeyAndVisible() } } final class DemoNavigationController: UINavigationController { // 与音剪一致:导航容器固定横向栏,不转发给子页面。 @available(iOS 27.1, *) override var preferredVerticalBarBehavior: UIVerticalBarBehavior { .disabled } @available(iOS 27.1, *) override var childForPreferredVerticalBarBehavior: UIViewController? { nil } } final class DemoController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground if title == "设置" { navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction { [weak self] _ in self?.dismiss(animated: true) }) } else { navigationItem.rightBarButtonItem = UIBarButtonItem( image: UIImage(systemName: "gearshape"), primaryAction: UIAction { [weak self] _ in let settings = DemoController() settings.title = "设置" let navigation = DemoNavigationController(rootViewController: settings) navigation.modalPresentationStyle = .fullScreen self?.present(navigation, animated: true) }) } } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() print("[DuoDemo] \(title ?? "") safe=\(view.safeAreaInsets) nav=\(navigationController?.navigationBar.frame ?? .zero) tab=\(tabBarController?.tabBar.frame ?? .zero)") } }
0
2
515
4w
UIScreen.screens.count differs on iPhone Duo sims depending on if app is IOS 26 SDK, or iOS 27 SDK
Hi there, My app uses UIScreen.screens.count (deprecated) to predict whether mirroring is happening on older iOS devices I noticed that the number of screens is different between iOS 26 SDK apps, and iOS 27 SDK apps. Can I expect that iOS 26 SDK apps will continue to return 1 screen like below for the iPhone Duo release?. I assume it's like this is to maximise compatibility. App built with SDK 26 on Xcode 27.1b1 iPhone Duo sim: UIScreen.screens.count == 1 App built with SDK 27 on Xcode 27.1b1 iPhone Duo sim: UIScreen.screens.count == 2 Thanks for the help! Sam
Topic: UI Frameworks SubTopic: UIKit Tags:
4
0
394
Oct ’26
iPhone Duo - globe button appeared in RC 1 - intended change or bug?
In Xcode 27.1 RC 1 the native keyboard layout has been changed and now it shows the globe button. It looks like a bug but I'm not sure. Is the keyboard going to display the globe button in the final release? On top of that, the keyboard extension tells not to show its own globe button, so it's another point confirming that this is RC 1 regression bug. Xcode 27.1 Beta 1: Xcode 27.1 RC 1:
3
0
1.1k
Oct ’26
SwiftData Sectioning iOS 27
I try Apple: Adopting SwiftData for a Core Data App . But I have error " 'init(filter:sort:transaction:sectionBy:)' requires the types '[Trip]' and 'SectionedResults<Array.Element, String>' (aka 'SectionedResults<Trip, String>') be equivalent" at code line Query(filter: filter, sort: [sortDescriptor], sectionBy: sectionBy)
Topic: UI Frameworks SubTopic: SwiftUI
1
0
383
Oct ’26
LiveCommunicationKit: Handle.displayName is never shown — the call UI and Recents show Handle.value
On iOS [27.x (build)], LiveCommunicationKit shows Handle.value on every system surface and ignores Handle.displayName. That forces a choice that CallKit doesn't: Put a stable account id in value (as WWDC26 session 226 recommends, so Recents redial can identify the person), and the raw id appears on the incoming call screen, the banner and in Recents. Put the person's name in value so the UI reads correctly, and a redial from Recents hands back only the name, which can't reliably identify anyone. Minimal reproduction let configuration = ConversationManager.Configuration( ringtoneName: nil, iconTemplateImageData: nil, maximumConversationGroups: 1, maximumConversationsPerConversationGroup: 1, includesConversationInRecents: true, supportsVideo: false, supportedHandleTypes: [.generic, .phoneNumber, .emailAddress] ) let manager = ConversationManager(configuration: configuration) let remote = Handle(type: .generic, value: "u-1234", displayName: "Jane Appleseed") try await manager.reportNewIncomingConversation( uuid: UUID(), update: Conversation.Update(members: [remote], capabilities: []) ) Expected: the incoming call UI and the Recents row show "Jane Appleseed". Session 226 says displayName is "what the system shows when it can't match the handle to a contact". Tapping the Recents row delivers an INStartCallIntent whose contact personHandle.value is u-1234. Actual: the full-screen ring, the foreground banner and the Recents row all show u-1234. Tapping the row does deliver INStartCallIntent with personHandle.value == "u-1234", so redial works, but the user never sees the name. What we tried, all with the same result (the value is shown): Handle.Kind set to .generic, .phoneNumber or .emailAddress, with every kind listed in supportedHandleTypes Conversation.Update(localMember:) set to the local user's handle activeRemoteMembers set to the remote handle, and localMember plus activeRemoteMembers together Donating an INInteraction (an INStartCallIntent whose INPerson has personHandle set to the id, displayName set to the name and customIdentifier set to the id) when the conversation ends. The donation succeeds, but the Recents row and the redial payload don't change. CallKit comparison, same device: CXCallUpdate.remoteHandle = CXHandle(type: .generic, value: "u-1234") with localizedCallerName = "Jane Appleseed" shows the name on the ring and in Recents. A redial from Recents delivers personHandle.value == "u-1234". That's the behaviour we expected from LiveCommunicationKit. Questions: Is Handle.displayName meant to be shown when the handle doesn't match a contact? If so, is there a configuration step we're missing? If this is a bug, is there a supported way with LiveCommunicationKit to show a name while keeping a stable identifier for Recents redial? Filed as FB24933340. Device: iPhone 15 pro, iOS [27.0.1].
4
0
513
Oct ’26
WARNING: Application performed a reentrant operation in its NSTableView delegate. This warning will become an assert in the future.
Can anyone help to understand what is causing this warning message on this SampleCode. it happens when data in table gets updated it happens when I have lot of items in table and search for lets say # 6543 and then clear the search. "WARNING: Application performed a reentrant operation in its NSTableView delegate. This warning will become an assert in the future." If there are just few items in table, the warning is not there. And it started when updated to Xcode Version 14 and upward import SwiftUI import Foundation @main struct TestProjectSWIFTApp: App {     @StateObject var mydata: MyData = MyData()     var body: some Scene {         WindowGroup {             ContentView()                 .environmentObject(mydata)         }              } } struct ContentView: View {     @EnvironmentObject var mydata: MyData     var body: some View {         NavigationView{             NavigationLink("TABLE",  destination: MyTable())                      }     } } struct User: Identifiable {     let id: UUID = UUID()     var name: String     var score: Int } struct MyTable: View {     @EnvironmentObject var mydata: MyData     @State private var sortOrder = [KeyPathComparator(\User.name)]     @State var searchText: String = ""     var searchResults : [User] {         if searchText.isEmpty {             return mydata.alldata         } else {             return mydata.alldata.filter{$0.name.localizedCaseInsensitiveContains(searchText)}         }     }          var body: some View {                  Button("Generate new data", action: {             mydata.updateTable()         })                  Table(searchResults, sortOrder: $sortOrder) {             TableColumn("Score"){                 Text("\(Double($0.score))")             }             TableColumn("user", value: \.name)         }         .onChange(of: sortOrder) {             mydata.alldata.sort(using: $0)         }         .searchable(text: $searchText)     } } @MainActor class MyData : ObservableObject{     @Published var alldata: [User] = []               init(){         self.setData()     }          func setData(){         alldata = [             User(name: "User 1", score: 95),             User(name: "User 2", score: 80),             User(name: "User 3", score: 85)         ]     }          func updateTable(){         var newallData: [User] = []         var x = 0         while x < 10000{             newallData.append(User(name: "User #\(x)", score:  Int.random(in: 10..<999999)))                          x = x + 1         }         alldata = newallData     }      }
18
5
5.3k
Oct ’26
Live Activities: no minute-granularity self-updating countdown text; custom DiscreteFormatStyle types fail to decode in the system renderer
Summary There is no supported way for a Live Activity to display a self-updating countdown at minute granularity in the form transit riders expect — a bare "7 min" that ticks to "6 min" — even though Apple's own apps (Timer in the Dynamic Island, Maps ETA) display exactly this form. The API that should make this possible — Text(_ input: TimeDataSource<Date>, format:) with a custom DiscreteFormatStyle (added in iOS 18) — compiles and archives, but the rendered activity fails at decode time in the system renderer, so every region of the Live Activity displays grey placeholder boxes. What I'm building An NYC subway departures app. The app's design language everywhere (in-app, home screen widgets via baked timeline entries) is "7 min" / "Now". Real-world fidelity is minute-level: riders don't think in mm:ss. The Live Activity is the only surface that cannot match this design, because it can only use self-updating text between content updates, and none of the built-in live formats produce it. Built-in options and why each falls short Text(timerInterval:countsDown:) — mm:ss only; no minute-granularity mode (this is FB23091094). Also reserves maximum width (FB23091111). Text(date, style: .relative) — always appends a second unit ("7 min, 30 sec") and counts up ambiguously (unsigned) after the date passes. .reference(to:allowedFields:maxFieldCount:) (iOS 18) — closest usable option, but only spelled-out wide units with a mandatory preposition: "in 7 minutes". No abbreviated/narrow width option exists on SystemFormatStyle.DateReference. .offset(to:) (iOS 18) — measures time since the anchor, so an upcoming departure renders as a negative value ("−7 minutes"); inverted for countdown use, and also wide-units only. The bug: custom DiscreteFormatStyle fails to decode in the renderer A custom format style solves this completely — mine rendered exact "7 min"/"Now" strings and, by holding the full departure list, even advanced to the next train the moment one departed, all without waking the app: @available(iOS 18.0, *) struct TrainCountdownFormat: DiscreteFormatStyle { var departures: [Date] var rank: Int // 0 = closest upcoming train var fullText: Bool // "7 min" vs "7m" func format(_ now: Date) -> String { let upcoming = departures.filter { $0 > now }.sorted() guard rank < upcoming.count else { return "—" } let totalSeconds = Int(upcoming[rank].timeIntervalSince(now)) if totalSeconds < 60 { return "Now" } let minutes = totalSeconds / 60 return fullText ? "\(minutes) min" : "\(minutes)m" } func discreteInput(after input: Date) -> Date? { departures.compactMap { departure -> Date? in let seconds = departure.timeIntervalSince(input) guard seconds > 0 else { return nil } let intoMinute = seconds.truncatingRemainder(dividingBy: 60) return input.addingTimeInterval(intoMinute > 0 ? intoMinute : 60) }.min() } func discreteInput(before input: Date) -> Date? { departures.compactMap { departure -> Date? in let seconds = departure.timeIntervalSince(input) guard seconds > 0 else { return departure < input ? departure : departure.addingTimeInterval(-60) } let intoMinute = seconds.truncatingRemainder(dividingBy: 60) return input.addingTimeInterval(intoMinute - 60) }.max() } } // In the ActivityConfiguration views: Text(.currentDate, format: TrainCountdownFormat(departures: dates, rank: 0, fullText: true)) This compiles and the activity is created, but every presentation (compact trailing, expanded, lock screen) renders as grey placeholder boxes. The console shows the archive being rejected at decode time in the renderer process: WidgetRenderer_Activities: (WidgetRenderer) [com.apple.chrono:activityRendererClient-verbose] Failed to return view entry from archive for view model with tag dynamicIsland-compactTrailing with error: SwiftUI.AnyCodable<...SafelyCodableRequirement>...Errors.noType(mangledName: "7SwiftUI18TimeDataFormattingO10ResolvableVy_AA0cD6SourceVAAE11DateStorageOy10Foundation0H0V_G 27NowDepartingWidgetExtension10$1030be9a0yXZ20TrainCountdownFormatV G") i.e. the archived TimeDataFormatting.Resolvable wrapper references a type defined in the app's widget extension, which the system renderer cannot look up. Nothing in the DiscreteFormatStyle or Text(_:format:) documentation states that only system-defined format styles are renderable in Live Activities, and there is no compile-time or runtime diagnostic surfaced to the developer — the activity just renders blank. Requests (either would unblock this) Support custom DiscreteFormatStyle types in Live Activity rendering (e.g. by evaluating the format in the extension's process when pre-rendering discrete frames), or at minimum document the limitation and fail loudly instead of rendering placeholder boxes. Add minute-granularity and unit-width options to the built-in live formats: a showsSeconds/fields option on Text(timerInterval:) (FB23091094), and/or a units-width option (abbreviated/narrow) plus a "bare duration, no preposition, countdown sign convention" variant on SystemFormatStyle.DateReference / DateOffset. Steps to reproduce Create a Live Activity whose views use Text(.currentDate, format:) with any custom DiscreteFormatStyle (sample above). Start the activity; background the app. Observe the Dynamic Island and lock screen render grey placeholder boxes for every region, and WidgetRenderer_Activities logs Errors.noType for each view model tag. Expected: the custom format renders and updates at the boundaries reported by discreteInput(before:/after:), as it does for the system styles. Actual: decode failure in the renderer; entire activity renders as placeholders. Environment Xcode 27.0 (27A266a) iOS 27.0 simulator, iPhone 18 Pro (also applies to iOS 18+ per API availability) Widget extension deployment target: iOS 17.6 Related reports: FB23091094, FB23091111, forum thread https://developer.apple.com/forums/thread/834337
1
0
131
Oct ’26
iOS 27: ScrollViewProxy.scrollTo no longer reaches unrealized LazyVStack rows; how to preserve position when prepending?
I have a chat screen built with ScrollView { LazyVStack } and ScrollViewReader. Two things that worked through iOS 26 stopped working on iOS 27 (Xcode 27, iOS 27.0 simulator and device; unchanged code still works on the iOS 26.x simulator): Open at the newest message. On appear I call proxy.scrollTo(lastId, anchor: .bottom). On iOS 27 the list stops two or three rows above the bottom when the rows have variable heights (text bubbles mixed with 200pt images). Load older messages at the top without the list jumping. When the user reaches the top, I prepend 25 older messages and call proxy.scrollTo(previousTopId, anchor: .top) so the row they were reading stays put. On iOS 27 the call does nothing: the list stays at the top of the newly inserted page (offset stays at 0), which immediately re-triggers the load. Re-issuing scrollTo on every layout change for a short window (which is what made this reliable on iOS 26) has no effect on iOS 27. Minimal reproduction import SwiftUI struct Message: Identifiable, Hashable { let id: Int let height: CGFloat // simulates text vs. image bubbles } @MainActor final class ChatModel: ObservableObject { @Published var messages: [Message] = [] @Published var previousTopId: Int? // set when a page is prepended private var nextOldId = 1_000 init() { messages = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 } func loadOlder() { let top = messages.first!.id let page = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 messages.insert(contentsOf: page, at: 0) previousTopId = top // "keep this row at the top" } static func randomHeight() -> CGFloat { [44, 60, 90, 200, 260].randomElement()! } } struct ChatView: View { @StateObject private var model = ChatModel() var body: some View { ScrollViewReader { proxy in ScrollView { LazyVStack(spacing: 8) { // top sentinel: load older when it becomes visible Color.clear.frame(height: 1) .onAppear { model.loadOlder() } ForEach(model.messages) { m in RoundedRectangle(cornerRadius: 12) .fill(m.height >= 200 ? .orange.opacity(0.4) : .blue.opacity(0.3)) .frame(height: m.height) .overlay(Text("\(m.id)")) .padding(.horizontal) .id(m.id) } } } .onAppear { // (1) open at the newest message DispatchQueue.main.async { proxy.scrollTo(model.messages.last!.id, anchor: .bottom) } } .onChange(of: model.previousTopId) { _, id in // (2) restore the row that was at the top before the prepend guard let id else { return } DispatchQueue.main.async { var t = Transaction(); t.disablesAnimations = true withTransaction(t) { proxy.scrollTo(id, anchor: .top) } } } } } } Observed (1) Open at the newest message, scrollTo(lastId, anchor: .bottom) iOS 26.x: lands on the last row. iOS 27.0: stops 2–3 rows above the bottom. (2) Prepend 25 rows, then scrollTo(previousTopId, anchor: .top) iOS 26.x: the previous top row is at the top of the viewport. iOS 27.0: the offset stays at 0 and the new page's first row is at the top. What I have tried on iOS 27 Re-issuing scrollTo for 0.5s on every content-height or offset change (via GeometryReader preferences). No effect; the target row is not realized, and the visible rows are kept stable instead. .defaultScrollAnchor(.bottom) and .defaultScrollAnchor(.bottom, for: .sizeChanges): fixes the initial open, but the stored anchor is re-applied once on the first content change (the prepend), which snaps the list to the newest message. .sizeChanges did not preserve the prepend. .scrollPosition(id: $topId, anchor: .top) with .scrollTargetLayout() on the LazyVStack: the binding tracks the top row correctly, but after the prepend SwiftUI re-targets the binding to the new page's first row. Writing the previous id back, in the same update or on later run-loop turns, does not restore the position. Replacing LazyVStack with VStack fixes both cases, but the list holds hundreds of image rows and needs the lazy container for memory. What does work: reaching the backing UIScrollView (SwiftUIIntrospect), recording the visible rows' frames before the insert and adjusting contentOffset after layout. It works but is a lot of code for something that used to be one scrollTo. Questions Is it intended on iOS 27 that ScrollViewProxy.scrollTo does not scroll to a LazyVStack row that is not currently realized, or that the lazy stack keeps the currently visible rows stable in preference to the requested target? The WWDC26 lazy-stacks session describes the stack and scroll view coordinating the offset as estimates update; is scrollTo to an unrealized row now unsupported? Is there a supported SwiftUI way on iOS 27 to keep the visible rows in place when items are prepended to a LazyVStack, or to scroll reliably to an unrealized row? For example a ScrollPosition usage or an anchor role I am missing. If not, is adjusting the UIScrollView offset the expected approach, or is List now the recommended container for chat-style lists with bidirectional paging? I have filed this as FB24968838 with the sample project attached.
1
0
473
Oct ’26
Toolbar background behaviour
Say i have a scrollview: I find it’s dumb how if you have the safe area inset or any kind of element below the toolbar, the background of those will extend to the top edge of the screen (by default), yet if the scrollview scrolls to the toolbar the toolbar background just “appears”, covering the existing background— its even worse cuz the toolbar background is there by default even when you didn’t set it in code. It seems more like a bug; I feel like this is something the SwiftUI team should improve upon. How about disabling toolbar background by default? Just wanted to get some more perspectives.
0
0
396
Oct ’26
How does the AppStore app create an extra long toolbar
not sure if you guys can see with just image, but the scroll content is able to show underneath the section with the segmented pickers, and the section the background blends totally smoothly with the toolbar. from my own attempts, I am not able to make a extra long toolbar, and if i added a row just underneath the toolbar, the effect of scrolling the content under the row does not produce the effect.. I'm just wondering how Apple made this. did they just built the toolbar custom without even using swiftui toolbar? yes i am aware this is an older version of iOS, but I still want to know. thank you.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
284
Oct ’26
Hard scroll edge effect
The example they gave for the hard scroll edge effect is the calendar app, but Idk if this is a bad example or if i am missing something, because the calendar app theres a date picker row below the toolbar, and from my testing, whenever theres some element between the toolbar and the scrollview, the fade and blur effect does not happen -- so in fact, the hard scroll edge effect is completely unnecessary.
0
0
126
Oct ’26
Layout glitch after rotation when using UIWindowScene sizeRestrictions on iPadOS 26
Hi everyone, I am experiencing a strange rendering issue on iPadOS 26 when sizeRestrictions.minimumSize is set on a UIWindowScene. After rotating the device and then rotating it back to the original orientation, the window appears to be stretched based on its previous dimensions. This resulting "stretched" area does not resize or redraw correctly, leaving a significant black region on the screen. Interestingly, as soon as I interact with the window (e.g., a slight drag or touch), the UI snaps back to its intended state and redraws perfectly. Here is a sample code and capture of behavior. class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } windowScene.sizeRestrictions?.minimumSize = CGSize( width: 390, height: 844 // larger than the height of iPad in landscape ) // initialize... } } Has anyone else encountered this behavior? If so, are there any known workarounds to force a layout refresh or prevent this "ghost" black area during the rotation transition? Any insights would be greatly appreciated. Thanks!
2
0
1.3k
Oct ’26
[tvOS] ScrollView with Text does not scroll
I'm trying to do something so seemingly basic, yet I can't get it to work and I'm flummoxed. In a basic, vanilla SwiftUI app for tvOS, embed a single Text element with a very long string (hundreds of lines) in it: struct ContentView: View { var body: some View { ScrollView(.vertical) { Text(veryLargeString) .focusable() } } } Then fire up the app on tvOS, and it will not scroll. No matter what I do. Pressing arrow keys, swiping fast with my thumb, and nothing. It will not move. Ironically, in the Xcode SwiftUI Preview window—it does scroll, so that's always a fun tease. What I do know is that the focus engine is throwing a few errors, so it's leading me to believe the issue is with how I have the focusable element attached. I'm using a combination of -UIFocusLoggingEnabled YES as well as listening for UIFocusSystem.movementDidFailNotification. Unfortunately since this is SwiftUI, the notification failure and debugging logs aren't really all that actionable. Help appreciated!
Replies
4
Boosts
4
Views
1.8k
Activity
4w
External Display stops working at iOS 27 / Xcode 27
I have an app that connects to an external display, the app controls content for the external display, but the external display is not active and uses the UIWindowSceneSessionRoleExternalDisplayNonInteractive role. I migrated the app to use UIScene lifecycle last year and everything seemed to be working. I use - (UISceneConfiguration *) application:(UIApplication *) application configurationForConnectingSceneSession:(UISceneSession *) connectingSceneSession options:(UISceneConnectionOptions *) options; in the AppDelegate to provide the configurations. But now at XCode 27/ iOS 27, that code is not called when I connect the external display with a dislpay adapter to my iPhone. But the device connects and shows a mirror of my iPhone display. What's going on?
Topic: UI Frameworks SubTopic: UIKit
Replies
2
Boosts
0
Views
174
Activity
4w
Save & Edit UIBarButtonSystemItems need to be updated for the iPhone Duo
When creating a UIBarButtonItem using the standard UIBarButtonSystemItem values of UIBarButtonSystemItemSave or UIBarButtonSystemEdit you end up with text-only buttons. The text-only buttons are an issue with the iPhone Duo since such buttons do not get put in the vertical sidebar. This is a problem with a view controller with scrollable content. The text-only Save or Edit buttons can scroll out of view leaving the user confused about how to complete their work on the screen. Related is the UIViewController editButtonItem which gives the standard Edit/Done toggle button. Since the button shows the text "Edit" the button doesn't get put in the vertical sidebar. This is despite the Done display being shown as a checkmark icon.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
457
Activity
4w
Best practice Replacement for Edit Button on iPhone Duo
On the iPhone Duo the standard "Edit" button for navigation bars is a text-based button, and text-based buttons are not pushed to the vertical toolbars on the Duo. This looks sometimes silly, if it is the only remaining button that can not be placed in the vertical toolbar and which has the result that an almost empty horizontal navigation bar must be kept visible as well. So I guess it would be good idea to have an icon-based replacement for the Editor button to avoid having two toolbars on the Duo. Is there a best practice for the icon to pick for such an edit button? Would be "square.and.pencil" (SF Symbols) a good choice? Or better the "pencil .circle", because the "square" one is often used for text-based input? Anything better?
Replies
4
Boosts
0
Views
1.3k
Activity
4w
Siri AI AppIntent snippets don’t size correctly and take up the full width in iOS 27.1 RC
I have been trying to create snippet views for my AppShortcuts which fill the full width of the container. Doing this via either .frame(minWidth: 0, maxWidth. infinity) or .containerRelativeFrame() work perfectly fine when my snippet is presented from Shortcuts.app (though sometimes the wrong color scheme is displayed), but when I invoke my AppShortcut from Siri AI and it presents the snippet there, it's offset on the trailing side by sometimes up to over 30 points, which has frustrating effects on the UI I am trying to implement. I've noted the insets are different too depending on whether the snippet is presented from what AppIntents method invokes it requestConfirmation or .result respectively. Here's some screenshots showing the issue. All these use the exact same snippet view, but all have different results in how they look and are inset. In App (how I want it to look) In Shortcuts (fine I guess) And finally, in the Siri app. I have reproduced this is issue and distilled it in a sample project. Please ensure this routes to the correct team: FB25164368. Thanks!
Replies
0
Boosts
0
Views
225
Activity
4w
UINavigationController, Nav Bar buttons incorrect layout on iPhone Duo
When presenting a UINavigationController presented via UIViewControllerRepresentable in a sheet, the top nav bar buttons aren't correctly moving to the side bar on the front screen of the Duo. They do re-orient vertically, but they do so next to the side bar and not within it. What am I doing wrong? This is how I am setting the buttons: let trimButton = UIBarButtonItem(image: UIImage(systemName: "selection.pin.in.out"), style: .plain, target: self, action: #selector(enterTrimming(_:))) navigationItem.leftBarButtonItem = trimButton navigationItem.rightBarButtonItem = cancelButton navigationItem.leftBarButtonItem?.isEnabled = true navigationItem.rightBarButtonItem?.isEnabled = true
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
211
Activity
4w
Vision 26.7
Apple's latest Vision 26.7, no longer recognizes Thai language( I haven't checked others), and crashes if request.recognitionLevel = .accurate Any ideas what is going on here, or just Apple's poor thoroughness at releasing an osx prematurely, as usual? let request = VNRecognizeTextRequest() request.recognitionLanguages = [language] if ProcessInfo.processInfo.isiOSAppOnMac { request.recognitionLevel = .fast // workaround for macOS 26.7.1 crash } else { request.recognitionLevel = .accurate } request.minimumTextHeight = 0
Replies
1
Boosts
0
Views
244
Activity
4w
How to automatically size a table's footer view
Hi, I would like to automatically vertically size the table's footer view so that it has a fixed pre-defined minimum height and maximum height that fills up the gap between the last cell and the bottom of the safe area. How can I do this? PS: It seems to be that the footer view's size is not included in the table view's content size, correct? Regards, Hardy
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
152
Activity
4w
iPhone Duo: navigation bar item briefly moves to vertical bar during fullScreen dismissal despite .disabled
On the folded iPhone Duo display, a navigation bar item briefly moves into the vertical bar during dismissal of a full-screen modal, then jumps back to the top. Both navigation controllers consistently return .disabled from preferredVerticalBarBehavior, with childForPreferredVerticalBarBehavior returning nil. Environment Xcode 27.1 RC (27A9275), iOS 27.1 SDK, iPhone Duo simulator, folded outer display in portrait. Steps to reproduce Run the single-file UIKit example below in a scene-based app with a generated launch screen and no main storyboard. Tap the gear button on the Home screen to present Settings using .fullScreen. Tap the close button on Settings. Watch the Home screen's gear button during the dismissal animation. Expected The gear button stays in the top horizontal navigation bar throughout the transition, because the vertical bar preference is .disabled on both navigation controllers. Actual For part of the dismissal, the gear button appears below the status icons in the right-hand vertical bar region. It then jumps back to the top horizontal bar. The underlying tab bar also temporarily moves from the bottom to the right before returning. This reproduces with an otherwise empty view, standard UIKit containers, and the default bar appearance. No app code changes safeAreaInsets, bar frames, or animation timing. During the presentation/dismissal cycle, the Home view's logged right safe-area inset changes from 0 to 88 points and back to 0. In our app, content constrained to the safe area visibly narrows during the same transient change. In separate comparisons, returning .disabled from the root tab controller and content controllers, or requesting setNeedsUpdateOfVerticalBarConfiguration in appearance callbacks, did not eliminate the issue. Changing the modal to .overFullScreen avoided it in the tested simulator, but changes the underlying view controller lifecycle. The example below intentionally keeps .fullScreen to reproduce the problem. Is this expected behavior, or a known UIKit issue? Is there a supported way to keep .disabled effective throughout a fullScreen presentation/dismissal? Has this been addressed in a newer SDK/runtime? I have not tested iOS 27.2 or a physical Duo device. Minimal reproduction (one Swift file): import UIKit @main final class DemoAppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let configuration = UISceneConfiguration(name: "Demo", sessionRole: session.role) configuration.delegateClass = DemoSceneDelegate.self return configuration } } final class DemoSceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options: UIScene.ConnectionOptions) { guard let scene = scene as? UIWindowScene else { return } let home = DemoController() home.title = "首页" let navigation = DemoNavigationController(rootViewController: home) navigation.tabBarItem = UITabBarItem(title: "首页", image: UIImage(systemName: "house"), selectedImage: nil) let tabs = UITabBarController() if #available(iOS 18.0, *) { tabs.mode = .tabBar } tabs.viewControllers = [navigation] let window = UIWindow(windowScene: scene) window.rootViewController = tabs self.window = window window.makeKeyAndVisible() } } final class DemoNavigationController: UINavigationController { // 与音剪一致:导航容器固定横向栏,不转发给子页面。 @available(iOS 27.1, *) override var preferredVerticalBarBehavior: UIVerticalBarBehavior { .disabled } @available(iOS 27.1, *) override var childForPreferredVerticalBarBehavior: UIViewController? { nil } } final class DemoController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground if title == "设置" { navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction { [weak self] _ in self?.dismiss(animated: true) }) } else { navigationItem.rightBarButtonItem = UIBarButtonItem( image: UIImage(systemName: "gearshape"), primaryAction: UIAction { [weak self] _ in let settings = DemoController() settings.title = "设置" let navigation = DemoNavigationController(rootViewController: settings) navigation.modalPresentationStyle = .fullScreen self?.present(navigation, animated: true) }) } } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() print("[DuoDemo] \(title ?? "") safe=\(view.safeAreaInsets) nav=\(navigationController?.navigationBar.frame ?? .zero) tab=\(tabBarController?.tabBar.frame ?? .zero)") } }
Replies
0
Boosts
2
Views
515
Activity
4w
UIScreen.screens.count differs on iPhone Duo sims depending on if app is IOS 26 SDK, or iOS 27 SDK
Hi there, My app uses UIScreen.screens.count (deprecated) to predict whether mirroring is happening on older iOS devices I noticed that the number of screens is different between iOS 26 SDK apps, and iOS 27 SDK apps. Can I expect that iOS 26 SDK apps will continue to return 1 screen like below for the iPhone Duo release?. I assume it's like this is to maximise compatibility. App built with SDK 26 on Xcode 27.1b1 iPhone Duo sim: UIScreen.screens.count == 1 App built with SDK 27 on Xcode 27.1b1 iPhone Duo sim: UIScreen.screens.count == 2 Thanks for the help! Sam
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
4
Boosts
0
Views
394
Activity
Oct ’26
iPhone Duo - globe button appeared in RC 1 - intended change or bug?
In Xcode 27.1 RC 1 the native keyboard layout has been changed and now it shows the globe button. It looks like a bug but I'm not sure. Is the keyboard going to display the globe button in the final release? On top of that, the keyboard extension tells not to show its own globe button, so it's another point confirming that this is RC 1 regression bug. Xcode 27.1 Beta 1: Xcode 27.1 RC 1:
Replies
3
Boosts
0
Views
1.1k
Activity
Oct ’26
SwiftData Sectioning iOS 27
I try Apple: Adopting SwiftData for a Core Data App . But I have error " 'init(filter:sort:transaction:sectionBy:)' requires the types '[Trip]' and 'SectionedResults<Array.Element, String>' (aka 'SectionedResults<Trip, String>') be equivalent" at code line Query(filter: filter, sort: [sortDescriptor], sectionBy: sectionBy)
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
383
Activity
Oct ’26
LiveCommunicationKit: Handle.displayName is never shown — the call UI and Recents show Handle.value
On iOS [27.x (build)], LiveCommunicationKit shows Handle.value on every system surface and ignores Handle.displayName. That forces a choice that CallKit doesn't: Put a stable account id in value (as WWDC26 session 226 recommends, so Recents redial can identify the person), and the raw id appears on the incoming call screen, the banner and in Recents. Put the person's name in value so the UI reads correctly, and a redial from Recents hands back only the name, which can't reliably identify anyone. Minimal reproduction let configuration = ConversationManager.Configuration( ringtoneName: nil, iconTemplateImageData: nil, maximumConversationGroups: 1, maximumConversationsPerConversationGroup: 1, includesConversationInRecents: true, supportsVideo: false, supportedHandleTypes: [.generic, .phoneNumber, .emailAddress] ) let manager = ConversationManager(configuration: configuration) let remote = Handle(type: .generic, value: "u-1234", displayName: "Jane Appleseed") try await manager.reportNewIncomingConversation( uuid: UUID(), update: Conversation.Update(members: [remote], capabilities: []) ) Expected: the incoming call UI and the Recents row show "Jane Appleseed". Session 226 says displayName is "what the system shows when it can't match the handle to a contact". Tapping the Recents row delivers an INStartCallIntent whose contact personHandle.value is u-1234. Actual: the full-screen ring, the foreground banner and the Recents row all show u-1234. Tapping the row does deliver INStartCallIntent with personHandle.value == "u-1234", so redial works, but the user never sees the name. What we tried, all with the same result (the value is shown): Handle.Kind set to .generic, .phoneNumber or .emailAddress, with every kind listed in supportedHandleTypes Conversation.Update(localMember:) set to the local user's handle activeRemoteMembers set to the remote handle, and localMember plus activeRemoteMembers together Donating an INInteraction (an INStartCallIntent whose INPerson has personHandle set to the id, displayName set to the name and customIdentifier set to the id) when the conversation ends. The donation succeeds, but the Recents row and the redial payload don't change. CallKit comparison, same device: CXCallUpdate.remoteHandle = CXHandle(type: .generic, value: "u-1234") with localizedCallerName = "Jane Appleseed" shows the name on the ring and in Recents. A redial from Recents delivers personHandle.value == "u-1234". That's the behaviour we expected from LiveCommunicationKit. Questions: Is Handle.displayName meant to be shown when the handle doesn't match a contact? If so, is there a configuration step we're missing? If this is a bug, is there a supported way with LiveCommunicationKit to show a name while keeping a stable identifier for Recents redial? Filed as FB24933340. Device: iPhone 15 pro, iOS [27.0.1].
Replies
4
Boosts
0
Views
513
Activity
Oct ’26
WARNING: Application performed a reentrant operation in its NSTableView delegate. This warning will become an assert in the future.
Can anyone help to understand what is causing this warning message on this SampleCode. it happens when data in table gets updated it happens when I have lot of items in table and search for lets say # 6543 and then clear the search. "WARNING: Application performed a reentrant operation in its NSTableView delegate. This warning will become an assert in the future." If there are just few items in table, the warning is not there. And it started when updated to Xcode Version 14 and upward import SwiftUI import Foundation @main struct TestProjectSWIFTApp: App {     @StateObject var mydata: MyData = MyData()     var body: some Scene {         WindowGroup {             ContentView()                 .environmentObject(mydata)         }              } } struct ContentView: View {     @EnvironmentObject var mydata: MyData     var body: some View {         NavigationView{             NavigationLink("TABLE",  destination: MyTable())                      }     } } struct User: Identifiable {     let id: UUID = UUID()     var name: String     var score: Int } struct MyTable: View {     @EnvironmentObject var mydata: MyData     @State private var sortOrder = [KeyPathComparator(\User.name)]     @State var searchText: String = ""     var searchResults : [User] {         if searchText.isEmpty {             return mydata.alldata         } else {             return mydata.alldata.filter{$0.name.localizedCaseInsensitiveContains(searchText)}         }     }          var body: some View {                  Button("Generate new data", action: {             mydata.updateTable()         })                  Table(searchResults, sortOrder: $sortOrder) {             TableColumn("Score"){                 Text("\(Double($0.score))")             }             TableColumn("user", value: \.name)         }         .onChange(of: sortOrder) {             mydata.alldata.sort(using: $0)         }         .searchable(text: $searchText)     } } @MainActor class MyData : ObservableObject{     @Published var alldata: [User] = []               init(){         self.setData()     }          func setData(){         alldata = [             User(name: "User 1", score: 95),             User(name: "User 2", score: 80),             User(name: "User 3", score: 85)         ]     }          func updateTable(){         var newallData: [User] = []         var x = 0         while x < 10000{             newallData.append(User(name: "User #\(x)", score:  Int.random(in: 10..<999999)))                          x = x + 1         }         alldata = newallData     }      }
Replies
18
Boosts
5
Views
5.3k
Activity
Oct ’26
Live Activities: no minute-granularity self-updating countdown text; custom DiscreteFormatStyle types fail to decode in the system renderer
Summary There is no supported way for a Live Activity to display a self-updating countdown at minute granularity in the form transit riders expect — a bare "7 min" that ticks to "6 min" — even though Apple's own apps (Timer in the Dynamic Island, Maps ETA) display exactly this form. The API that should make this possible — Text(_ input: TimeDataSource<Date>, format:) with a custom DiscreteFormatStyle (added in iOS 18) — compiles and archives, but the rendered activity fails at decode time in the system renderer, so every region of the Live Activity displays grey placeholder boxes. What I'm building An NYC subway departures app. The app's design language everywhere (in-app, home screen widgets via baked timeline entries) is "7 min" / "Now". Real-world fidelity is minute-level: riders don't think in mm:ss. The Live Activity is the only surface that cannot match this design, because it can only use self-updating text between content updates, and none of the built-in live formats produce it. Built-in options and why each falls short Text(timerInterval:countsDown:) — mm:ss only; no minute-granularity mode (this is FB23091094). Also reserves maximum width (FB23091111). Text(date, style: .relative) — always appends a second unit ("7 min, 30 sec") and counts up ambiguously (unsigned) after the date passes. .reference(to:allowedFields:maxFieldCount:) (iOS 18) — closest usable option, but only spelled-out wide units with a mandatory preposition: "in 7 minutes". No abbreviated/narrow width option exists on SystemFormatStyle.DateReference. .offset(to:) (iOS 18) — measures time since the anchor, so an upcoming departure renders as a negative value ("−7 minutes"); inverted for countdown use, and also wide-units only. The bug: custom DiscreteFormatStyle fails to decode in the renderer A custom format style solves this completely — mine rendered exact "7 min"/"Now" strings and, by holding the full departure list, even advanced to the next train the moment one departed, all without waking the app: @available(iOS 18.0, *) struct TrainCountdownFormat: DiscreteFormatStyle { var departures: [Date] var rank: Int // 0 = closest upcoming train var fullText: Bool // "7 min" vs "7m" func format(_ now: Date) -> String { let upcoming = departures.filter { $0 > now }.sorted() guard rank < upcoming.count else { return "—" } let totalSeconds = Int(upcoming[rank].timeIntervalSince(now)) if totalSeconds < 60 { return "Now" } let minutes = totalSeconds / 60 return fullText ? "\(minutes) min" : "\(minutes)m" } func discreteInput(after input: Date) -> Date? { departures.compactMap { departure -> Date? in let seconds = departure.timeIntervalSince(input) guard seconds > 0 else { return nil } let intoMinute = seconds.truncatingRemainder(dividingBy: 60) return input.addingTimeInterval(intoMinute > 0 ? intoMinute : 60) }.min() } func discreteInput(before input: Date) -> Date? { departures.compactMap { departure -> Date? in let seconds = departure.timeIntervalSince(input) guard seconds > 0 else { return departure < input ? departure : departure.addingTimeInterval(-60) } let intoMinute = seconds.truncatingRemainder(dividingBy: 60) return input.addingTimeInterval(intoMinute - 60) }.max() } } // In the ActivityConfiguration views: Text(.currentDate, format: TrainCountdownFormat(departures: dates, rank: 0, fullText: true)) This compiles and the activity is created, but every presentation (compact trailing, expanded, lock screen) renders as grey placeholder boxes. The console shows the archive being rejected at decode time in the renderer process: WidgetRenderer_Activities: (WidgetRenderer) [com.apple.chrono:activityRendererClient-verbose] Failed to return view entry from archive for view model with tag dynamicIsland-compactTrailing with error: SwiftUI.AnyCodable<...SafelyCodableRequirement>...Errors.noType(mangledName: "7SwiftUI18TimeDataFormattingO10ResolvableVy_AA0cD6SourceVAAE11DateStorageOy10Foundation0H0V_G 27NowDepartingWidgetExtension10$1030be9a0yXZ20TrainCountdownFormatV G") i.e. the archived TimeDataFormatting.Resolvable wrapper references a type defined in the app's widget extension, which the system renderer cannot look up. Nothing in the DiscreteFormatStyle or Text(_:format:) documentation states that only system-defined format styles are renderable in Live Activities, and there is no compile-time or runtime diagnostic surfaced to the developer — the activity just renders blank. Requests (either would unblock this) Support custom DiscreteFormatStyle types in Live Activity rendering (e.g. by evaluating the format in the extension's process when pre-rendering discrete frames), or at minimum document the limitation and fail loudly instead of rendering placeholder boxes. Add minute-granularity and unit-width options to the built-in live formats: a showsSeconds/fields option on Text(timerInterval:) (FB23091094), and/or a units-width option (abbreviated/narrow) plus a "bare duration, no preposition, countdown sign convention" variant on SystemFormatStyle.DateReference / DateOffset. Steps to reproduce Create a Live Activity whose views use Text(.currentDate, format:) with any custom DiscreteFormatStyle (sample above). Start the activity; background the app. Observe the Dynamic Island and lock screen render grey placeholder boxes for every region, and WidgetRenderer_Activities logs Errors.noType for each view model tag. Expected: the custom format renders and updates at the boundaries reported by discreteInput(before:/after:), as it does for the system styles. Actual: decode failure in the renderer; entire activity renders as placeholders. Environment Xcode 27.0 (27A266a) iOS 27.0 simulator, iPhone 18 Pro (also applies to iOS 18+ per API availability) Widget extension deployment target: iOS 17.6 Related reports: FB23091094, FB23091111, forum thread https://developer.apple.com/forums/thread/834337
Replies
1
Boosts
0
Views
131
Activity
Oct ’26
iOS 27: ScrollViewProxy.scrollTo no longer reaches unrealized LazyVStack rows; how to preserve position when prepending?
I have a chat screen built with ScrollView { LazyVStack } and ScrollViewReader. Two things that worked through iOS 26 stopped working on iOS 27 (Xcode 27, iOS 27.0 simulator and device; unchanged code still works on the iOS 26.x simulator): Open at the newest message. On appear I call proxy.scrollTo(lastId, anchor: .bottom). On iOS 27 the list stops two or three rows above the bottom when the rows have variable heights (text bubbles mixed with 200pt images). Load older messages at the top without the list jumping. When the user reaches the top, I prepend 25 older messages and call proxy.scrollTo(previousTopId, anchor: .top) so the row they were reading stays put. On iOS 27 the call does nothing: the list stays at the top of the newly inserted page (offset stays at 0), which immediately re-triggers the load. Re-issuing scrollTo on every layout change for a short window (which is what made this reliable on iOS 26) has no effect on iOS 27. Minimal reproduction import SwiftUI struct Message: Identifiable, Hashable { let id: Int let height: CGFloat // simulates text vs. image bubbles } @MainActor final class ChatModel: ObservableObject { @Published var messages: [Message] = [] @Published var previousTopId: Int? // set when a page is prepended private var nextOldId = 1_000 init() { messages = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 } func loadOlder() { let top = messages.first!.id let page = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 messages.insert(contentsOf: page, at: 0) previousTopId = top // "keep this row at the top" } static func randomHeight() -> CGFloat { [44, 60, 90, 200, 260].randomElement()! } } struct ChatView: View { @StateObject private var model = ChatModel() var body: some View { ScrollViewReader { proxy in ScrollView { LazyVStack(spacing: 8) { // top sentinel: load older when it becomes visible Color.clear.frame(height: 1) .onAppear { model.loadOlder() } ForEach(model.messages) { m in RoundedRectangle(cornerRadius: 12) .fill(m.height >= 200 ? .orange.opacity(0.4) : .blue.opacity(0.3)) .frame(height: m.height) .overlay(Text("\(m.id)")) .padding(.horizontal) .id(m.id) } } } .onAppear { // (1) open at the newest message DispatchQueue.main.async { proxy.scrollTo(model.messages.last!.id, anchor: .bottom) } } .onChange(of: model.previousTopId) { _, id in // (2) restore the row that was at the top before the prepend guard let id else { return } DispatchQueue.main.async { var t = Transaction(); t.disablesAnimations = true withTransaction(t) { proxy.scrollTo(id, anchor: .top) } } } } } } Observed (1) Open at the newest message, scrollTo(lastId, anchor: .bottom) iOS 26.x: lands on the last row. iOS 27.0: stops 2–3 rows above the bottom. (2) Prepend 25 rows, then scrollTo(previousTopId, anchor: .top) iOS 26.x: the previous top row is at the top of the viewport. iOS 27.0: the offset stays at 0 and the new page's first row is at the top. What I have tried on iOS 27 Re-issuing scrollTo for 0.5s on every content-height or offset change (via GeometryReader preferences). No effect; the target row is not realized, and the visible rows are kept stable instead. .defaultScrollAnchor(.bottom) and .defaultScrollAnchor(.bottom, for: .sizeChanges): fixes the initial open, but the stored anchor is re-applied once on the first content change (the prepend), which snaps the list to the newest message. .sizeChanges did not preserve the prepend. .scrollPosition(id: $topId, anchor: .top) with .scrollTargetLayout() on the LazyVStack: the binding tracks the top row correctly, but after the prepend SwiftUI re-targets the binding to the new page's first row. Writing the previous id back, in the same update or on later run-loop turns, does not restore the position. Replacing LazyVStack with VStack fixes both cases, but the list holds hundreds of image rows and needs the lazy container for memory. What does work: reaching the backing UIScrollView (SwiftUIIntrospect), recording the visible rows' frames before the insert and adjusting contentOffset after layout. It works but is a lot of code for something that used to be one scrollTo. Questions Is it intended on iOS 27 that ScrollViewProxy.scrollTo does not scroll to a LazyVStack row that is not currently realized, or that the lazy stack keeps the currently visible rows stable in preference to the requested target? The WWDC26 lazy-stacks session describes the stack and scroll view coordinating the offset as estimates update; is scrollTo to an unrealized row now unsupported? Is there a supported SwiftUI way on iOS 27 to keep the visible rows in place when items are prepended to a LazyVStack, or to scroll reliably to an unrealized row? For example a ScrollPosition usage or an anchor role I am missing. If not, is adjusting the UIScrollView offset the expected approach, or is List now the recommended container for chat-style lists with bidirectional paging? I have filed this as FB24968838 with the sample project attached.
Replies
1
Boosts
0
Views
473
Activity
Oct ’26
Toolbar background behaviour
Say i have a scrollview: I find it’s dumb how if you have the safe area inset or any kind of element below the toolbar, the background of those will extend to the top edge of the screen (by default), yet if the scrollview scrolls to the toolbar the toolbar background just “appears”, covering the existing background— its even worse cuz the toolbar background is there by default even when you didn’t set it in code. It seems more like a bug; I feel like this is something the SwiftUI team should improve upon. How about disabling toolbar background by default? Just wanted to get some more perspectives.
Replies
0
Boosts
0
Views
396
Activity
Oct ’26
How does the AppStore app create an extra long toolbar
not sure if you guys can see with just image, but the scroll content is able to show underneath the section with the segmented pickers, and the section the background blends totally smoothly with the toolbar. from my own attempts, I am not able to make a extra long toolbar, and if i added a row just underneath the toolbar, the effect of scrolling the content under the row does not produce the effect.. I'm just wondering how Apple made this. did they just built the toolbar custom without even using swiftui toolbar? yes i am aware this is an older version of iOS, but I still want to know. thank you.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
284
Activity
Oct ’26
Hard scroll edge effect
The example they gave for the hard scroll edge effect is the calendar app, but Idk if this is a bad example or if i am missing something, because the calendar app theres a date picker row below the toolbar, and from my testing, whenever theres some element between the toolbar and the scrollview, the fade and blur effect does not happen -- so in fact, the hard scroll edge effect is completely unnecessary.
Replies
0
Boosts
0
Views
126
Activity
Oct ’26
Layout glitch after rotation when using UIWindowScene sizeRestrictions on iPadOS 26
Hi everyone, I am experiencing a strange rendering issue on iPadOS 26 when sizeRestrictions.minimumSize is set on a UIWindowScene. After rotating the device and then rotating it back to the original orientation, the window appears to be stretched based on its previous dimensions. This resulting "stretched" area does not resize or redraw correctly, leaving a significant black region on the screen. Interestingly, as soon as I interact with the window (e.g., a slight drag or touch), the UI snaps back to its intended state and redraws perfectly. Here is a sample code and capture of behavior. class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } windowScene.sizeRestrictions?.minimumSize = CGSize( width: 390, height: 844 // larger than the height of iPad in landscape ) // initialize... } } Has anyone else encountered this behavior? If so, are there any known workarounds to force a layout refresh or prevent this "ghost" black area during the rotation transition? Any insights would be greatly appreciated. Thanks!
Replies
2
Boosts
0
Views
1.3k
Activity
Oct ’26