Post

Replies

Boosts

Views

Activity

Reply to Verifying braille output in an iOS app without a physical braille device?
@Systems Engineer, I'm willing to drop back to UIKit if it solved the VoiceOver/braille display issues. Any pointers to an example UIKit implementation of AXMathExpressionProvider? There’s not much to go on in that doc or its related topics. I even tried deep-research mode in both Gemini and Claude Opus. Gemini flat-out gave up, while Claude surfaced this after reviewing 297 sources: The AXMathExpressionProvider protocol does not exist in the current iOS/SwiftUI accessibility APIs. After extensive research across Apple’s documentation, developer forums, GitHub repos, and WWDC sessions, there’s no evidence this protocol exists in the current iOS SDK. It may be hypothetical, planned for the future, or confused with other accessibility APIs.
2w
Reply to Verifying braille output in an iOS app without a physical braille device?
[Since I haven’t heard back, I’m re-posting my comment as a thread reply in case it got overlooked—comments can be easy to miss in this forum.] @Systems Engineer, thanks for the reply! Any pointers to a sample implementation of AXMathExpressionProvider? That doc is pretty thin and doesn't show examples…I learn best through examples! GitHub searches haven't turned up anything useful either. :( Also, my app is a SwiftUI iOS app—can I implement this with SwiftUI alone, or will I need UIKit?
2w
Reply to Verifying braille output in an iOS app without a physical braille device?
After some digging (and banging my head against the wall), I narrowed things down to two core issues. I’ve posted them as new topics since this thread title no longer fits and I can't update it. How to force VoiceOver to read decimal point even when there are 6 or more decimal digits? Programmatically force VoiceOver to read parentheses for math expressions
4w
Reply to VoiceOver and currency - high amounts
A similar thing happens with six or more decimal digits. For example: Text("0.12345") -> "zero point one two three four five" But this: Text("0.123456") -> "zero one, two, three, four, five, six" (commas represent pauses) How can I force VoiceOver to announce the decimal separator ("point") and not insert pauses regardless of the number of decimal digits?
4w
Reply to Verifying braille output in an iOS app without a physical braille device?
The only workaround I could find requires people to create a custom punctuation group that includes only mathematical symbols, then manually switch to it when using my app. This feels like a significant burden and seems at odds with providing a seamless experience. This highlights what feels like a gap in the accessibility API: no way for developers to precisely control VoiceOver speech for technical content (pronounce specific symbols but ensure natural number reading) while simultaneously ensuring literal display on Braille devices. I'm hoping an Apple accessibility engineer or experienced developer might know a better approach than requiring users to modify system-level settings for one app.
Jun ’25
Reply to Verifying braille output in an iOS app without a physical braille device?
Okay, after more research and reading these articles: Use a braille display with VoiceOver on iPhone Change your VoiceOver settings on iPhone It sounds like braille displays just use the VoiceOver output and whether it "reads" parentheses is part of the device's Verbosity › Punctuation setting. Then looking into whether I can set that programmatically, I came across this .speechAlwaysIncludesPuncturation() modifier, which may do exactly what I want. I'll give that a try report back! 🤞
Jun ’25
Reply to Product Page Optimization regression: App Icon tab no longer available
UPDATE: Without me doing anything, the App Icon tab showed up today. Not sure what caused the problem, but I did notice App Store Connect had performance issues in its system status, so perhaps some maintenance was going on earlier that caused the tabs disappearance. 🤷 Either way, I'm glad I was finally able to submit my optimization test.
Jun ’25
Reply to Simple SwiftData app exhibits excessive & persistent memory growth as items are added
Just wanted to close the loop on this…I ended up submitting a dev support ticket and was told that this issue was fixed in iOS 18 (beta at the time). After testing on iOS 18, I confirmed that they did address the lag issue. The good news: the SwiftData version remains responsive, even with 3,200 items. The bad news: it still uses twice the memory as the CoreData version. So I've decided to stay with CoreData in my app until there’s a compelling reason to switch back.
Nov ’24
Reply to How to build a top/bottom split view with a dynamically-sized divider?
I finally figured it out—this code achieves the behavior I was looking for: import SwiftUI enum SnapPoint: CGFloat, CaseIterable { case top = 0.0 case partial = 0.33 case bottom = 1.0 var value: CGFloat { return self.rawValue } static let allValues: [CGFloat] = SnapPoint.allCases.map { $0.rawValue } } struct ContentView: View { @State private var totalHeight: CGFloat = 0 @State private var topHeight: CGFloat = 0 @State private var dividerHeight: CGFloat = 0 @State private var showMore: Bool = true @State private var currentSnapPoint: SnapPoint = .partial @State private var previousDividerHeight: CGFloat = 0 let snapPoints: [SnapPoint] = SnapPoint.allCases var body: some View { GeometryReader { geometry in VStack(spacing: 0) { TopView() .frame(maxWidth: .infinity) .frame(height: max(0, topHeight)) .background(Color.red.opacity(0.3)) .border(.pink) .clipped() DividerView(showMore: $showMore) .zIndex(1) .background( GeometryReader { dividerGeometry in Color.clear .onAppear { dividerHeight = dividerGeometry.size.height if totalHeight == 0 { totalHeight = geometry.size.height topHeight = calculate(.partial) } previousDividerHeight = dividerHeight } .onChange(of: dividerGeometry.size.height) { withAnimation(.snappy(duration: 0.2)) { let deltaHeight = dividerGeometry.size.height - previousDividerHeight previousDividerHeight = dividerGeometry.size.height dividerHeight = dividerGeometry.size.height if currentSnapPoint != .top { topHeight = max(0, topHeight - deltaHeight) } if totalHeight == 0 { totalHeight = geometry.size.height topHeight = (totalHeight - dividerHeight) / 2 } } } } ) .gesture( DragGesture() .onChanged { value in topHeight = calculateDraggedTopHeight(value.translation.height) } .onEnded { _ in withAnimation(.snappy(duration: 0.2)) { let (snapPoint, height) = nearestSnapPoint(for: topHeight) topHeight = height currentSnapPoint = snapPoint } } ) BottomView() .frame(maxWidth: .infinity) .frame(height: max(0, geometry.size.height - topHeight - dividerHeight)) .background(Color.green.opacity(0.3)) .border(.pink) .clipped() } .onChange(of: geometry.size.height) { totalHeight = geometry.size.height topHeight = min(topHeight, totalHeight - dividerHeight) } } } func calculateDraggedTopHeight(_ translation: CGFloat) -> CGFloat { return max(0, min(topHeight + translation, totalHeight - dividerHeight)) } func nearestSnapPoint(for height: CGFloat) -> (SnapPoint, CGFloat) { let calculatedPoints = snapPoints.map { ($0, calculate($0)) } let nearest = calculatedPoints.min(by: { abs($0.1 - height) < abs($1.1 - height) }) ?? (.partial, height) return nearest } func calculate(_ point: SnapPoint) -> CGFloat { switch point { case .top: return 0 case .partial: return (totalHeight * point.value) - dividerHeight case .bottom: return totalHeight - dividerHeight } } } struct DividerView: View { @Binding var showMore: Bool var body: some View { VStack(spacing: 0) { Text(showMore ? "Tap to hide 'More'" : "Tap to show 'More'") .padding(16) .multilineTextAlignment(.center) if showMore { Text("More") .padding(16) } } .frame(maxWidth: .infinity) .background(Color(.systemBackground)) .onTapGesture { withAnimation(.snappy(duration: 0.2)) { showMore.toggle() } } } } struct TopView: View { var body: some View { Text("Top") } } struct BottomView: View { var body: some View { Text("Bottom") } } #Preview { ContentView() }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Oct ’24