I have a ScrollView which has inside a LazyVStack and multiple rows. On the appearing of the ScrollView, I scroll to row 250.
When having the VoiceOver running, after opening the app, the focus is on the navigation title as it should be, than, after swiping right to enter the ScrollView, one would expect the focus to be placed on the first visible row, but this is not the case, it scrolls back to some row and focus on that, than reads it.
Here is the code to reproduce it, and if you ask me, this is pretty standard stuff I don't do anything special.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollViewReader { scrollProxy in
ScrollView {
LazyVStack {
ForEach(1...500, id: \.self) { index in
NavigationLink(destination: DetailView(row: index)) {
Text("Row \(index)")
.padding()
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
.padding(.horizontal)
}
}
}
}
.onAppear {
// Scroll to row 250 when the view appears
withAnimation {
scrollProxy.scrollTo(250, anchor: .center)
}
}
}
.navigationTitle("Rows")
}
}
}
struct DetailView: View {
let row: Int
var body: some View {
VStack {
Text("Detail for Row \(row)")
.font(.largeTitle)
.padding()
Spacer()
}
.navigationTitle("Row \(row) Details")
}
}
@main
struct ScrollViewExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Post
Replies
Boosts
Views
Activity
On iOS 15 there is an issue with scrollTo of ScrollViewReader, it is scrolling too much.
I have a button and a date picker, when tapping the button I show the date picker but I want to make sure the date picker is fully visible on the screen, so I scroll to it. The code to reproduce the issue is attached below:
import SwiftUI
struct ScrollToIssue: View {
@State var showPicker: Bool = false
@State var date: Date = Date()
var body: some View {
ScrollViewReader { scrollViewReader in
ScrollView {
Color.red.frame(height: 400)
Color.yellow.frame(height: 200)
VStack {
Button(action: {
showPicker.toggle()
if showPicker {
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
withAnimation {
scrollViewReader.scrollTo("pickerrrr")
}
})
}
}, label: {
Text("Show picker")
})
if showPicker {
DatePicker(selection: $date,
displayedComponents: .date,
label: { EmptyView() })
.labelsHidden()
.datePickerStyle(GraphicalDatePickerStyle())
.id("pickerrrr")
}
}
.border(Color.green)
Color.blue.frame(height: 500)
}
}
}
}
On iOS 16/17, is working fine. Also tried to move the ScrollViewReader inside/outside of ScrollView, makes no difference, also tried with anchor .bottom, still same issue.
I used Xcode 15.0 with Iphone 13, iOS 15.5
The app on which I am working has it's ui tested using Appium. Because of this, I set accessibility identifiers on views.
struct RootView: View {
var body: some View {
VStack {
Text("some text")
Text("more text")
}
// wrapper 1
.accessibilityElement(children: .contain)
.accessibilityIdentifier("ID 1111111111111")
// wrapper 2
.accessibilityElement(children: .contain)
.accessibilityIdentifier("ID 2222222222222")
}
}
(the code is oversimplified to ease the understanding of the problem)
Running the code above on iOS 14 would output this on Appium Inspector when inspecting the views:
<XCUIElementTypeOther type="XCUIElementTypeOther" name="ID 2222222222222" enabled="true" visible="true" accessible="false" x="157" y="408" width="76" height="41" index="0">
<XCUIElementTypeOther type="XCUIElementTypeOther" name="ID 1111111111111" enabled="true" visible="true" accessible="false" x="157" y="408" width="76" height="41" index="0">
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="some text" name="some text" label="some text" enabled="true" visible="true" accessible="true" x="157" y="408" width="76" height="21" index="0"/>
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="more text" name="more text" label="more text" enabled="true" visible="true" accessible="true" x="159" y="428" width="73" height="21" index="1"/>
</XCUIElementTypeOther>
</XCUIElementTypeOther>
So each group of accessibilityElement and accessibilityIdentifier would create a XCUIElementTypeOther with the same name property as the id set by accessibilityIdentifier.
With iOS 15/16 these are not working anymore, here is the output on Appium Inspector when inspecting the views:
<XCUIElementTypeOther type="XCUIElementTypeOther" name="ID 2222222222222" enabled="true" visible="true" accessible="false" x="157" y="408" width="76" height="41" index="0">
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="some text" name="some text" label="some text" enabled="true" visible="true" accessible="true" x="157" y="408" width="76" height="21" index="0"/>
<XCUIElementTypeStaticText type="XCUIElementTypeStaticText" value="more text" name="more text" label="more text" enabled="true" visible="true" accessible="true" x="158" y="428" width="74" height="21" index="1"/>
</XCUIElementTypeOther>
It looks like the first group of accessibilityElement and accessibilityIdentifier is no longer generating an XCUIElementTypeOther.
Is there a way to have the same output of iOS 14 on iOS 15/16?
Is it possible for iPadOS to control the position of the popover, to appear on the right side of the view, or under, basically the same behaviour that arrowEdge is offering for macOS.
Right now it is always placed above of the view, like in the picture below. I would like to place it on the right side of the view, where the "red" popover is placed
The code
struct PopoverExample: View {
@State private var isShowingPopover = false
var body: some View {
Button("Show Popover") {
self.isShowingPopover = true
}
.popover(isPresented: $isShowingPopover, arrowEdge: .trailing) {
Text("Popover Content")
.padding()
}
}
}
With iOS 16, the toolbar and toolbarItem are working differently. On iOS 14/15, when adding two toolbars one after another, the second one overrides the first one. With iOS 16, the content from the second one extends the content of the first one. What I tried was to set ids for toolbar and toolbarItem hoping will have the same behaviour as on iOS 14/15 but it's not really working.
Desired behaviour is the one from iOS 14/15 and it looks like this:
Current behaviour for iOS 16:
This is my code which is working ok for iOS 14/15:
struct RootView: View {
var body: some View {
NavigationView {
Text("Hello, World!").padding()
.navigationTitle("navigation title")
.navigationBarTitleDisplayMode(.inline)
.toolbar(id: "toolbar_id") {
ToolbarItem(id: "toolbaritem_id", placement: .navigationBarLeading) {
Button("Button 1") {
print("Pressed Button 1")
}
}
}
.toolbar(id: "toolbar_id") {
ToolbarItem(id: "toolbaritem_id", placement: .navigationBarLeading) {
Button("Button 2") {
print("Pressed Button 2")
}
}
}
}
.navigationViewStyle(.stack)
}
}
Setting a navigationTitle is not working anymore on iOS 16 when having UINavigationController inside a TabView. Run the code with iOS 14/15, no issue there. If Tabview is commented, navigation title appears for iOS 16 too. It seems the problem is caused somehow by the TabView. I know I can send the title as a parameter but I would prefer not to, also, for the moment, switching to NavigationVies is not an option.
import SwiftUI
@main
struct CustomUIKitNavigationApp: App {
var body: some Scene {
WindowGroup {
TabView {
NavigationViewControllerRepresentable {
VStack {
Text("why navigation title is not working anymore on iOS 16 when in TabView?")
.navigationTitle("navigation is not appearing")
}
}
}
}
}
}
struct NavigationViewControllerRepresentable<Content: View>: UIViewControllerRepresentable {
let nav = UINavigationController()
init(@ViewBuilder content: @escaping () -> Content) {
let vc = HostingController(content: AnyView(content()))
nav.addChild(vc)
}
func makeUIViewController(context: Context) -> UINavigationController {
return nav
}
func updateUIViewController(_ pageViewController: UINavigationController, context: Context) {}
}
class HostingController: UIHostingController<AnyView> {
init(content: AnyView) {
super.init(rootView: AnyView(content))
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) not implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
As far as I was aware, there was no way to prevent, from Notification Service Extension, a remote notification to be displayed to the user.
It seems that from iOS 13.3 this is not the case anymore. You can request to Apple, a Notification Service Entitlement which will allow you, if entitlement is granted, to not display the notification.
You can find the link to the documentation here:
https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_usernotifications_filtering
Since this is not default behaviour, and Apple needs to grant this, I would speculate there are some conditions from Apple to grant this entitlement.
What are these conditions, if exists?
If Apple will grant the entitlement, do I still get that 30 seconds of processing? Let's say in that time I can send a request to some server and based on that response I could create some local notifications.
I don't understand the difference between closing an app and killing it from the user point if view.
As far as I know if I want to close an app I will proceed like explained here https://support.apple.com/en-us/HT201330
But if I want to kill the app, how me, as a user, can do this? I know for android if you search in settings the app there is a button called force stop.
Also, for example, when receiving Background Push Notification there is this concept of force quitting an app
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
To find out if he clears a single notification I can use the categoryIdentifier and actionIdentifier and and userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: u/escaping () - Void) gets called.
Is there a method that gets called when the "clear" button from notification center is used? The result of this action on the button is that all notifications get cleared.
I want on that call to reset badge number to 0.