How to set an accessibility identifier properly in SwiftUI? - ios

I would like to be able to tap a concrete button on my stack of views. To achieve this, first I set on my code an accessibility id:
Button(action: {
withAnimation(.easeOut(duration: 1)) {
self.modalShown.toggle()
}
}) {
FilterButton()
.accessibility(identifier: "modalFilterViewBtn")
}
.buttonStyle(PlainButtonStyle())
}
So, then on my text case I tried to look for that id:
IOSElement elem1 = driver.findElementByAccessibilityId("modalFilterViewBtn");
wait.until(ExpectedConditions.elementToBeClickable(elem1));
elem1.click();
And I obtain as a result: An element could not be located on the page using the given search parameters.(..)
Finally if I use the Appium inspector to know the accessibility id of my desired element, it seems to be another name that is not the same that I had set in code:
Even if I look for the element with that id("Filter"), it also can not be found during my test case.
So, what is the correct way to set this accessibility id using SwiftUI?
Thank you

I assume it should be like
Button(action: {
// action here
}) {
// button label view here
}
.buttonStyle(PlainButtonStyle())
.accessibility(identifier: "modalFilterViewBtn")
Note in iOS 14+ there is .accessibilityIdentifier(_ identifier: String) instead. accessibility(identifier: String) is deprecated.

Related

How can I customize the appearance of PasteButton in SwiftUI?

I have an app that allows the user to paste an image from the pasteboard, if it has an image on it. There is also an option for the user to choose a photo from the library. I had two buttons with a matching style, like this:
struct ButtonView: View {
var body: some View {
VStack {
Button {
// Paste from pasteboard
} label: {
HStack {
Image(systemName: "doc.on.clipboard")
Text("Paste Photo")
}
.font(.title)
}
.padding(.bottom, 8)
Button
{
// Open photo library
} label: {
Image(systemName: "photo.on.rectangle.angled")
Text("Pick a Photo")
}
.font(.title)
}
}
}
I'm trying to adopt the new PasteButton in iOS 16 to avoid the user prompt every time, but I can't find any documentation about how to customize it. In the WWDC 2022 session What's new in privacy, there is a section about the button and the speaker says "customize these buttons to fit with your app's interface," but I can't find any documentation about how to customize or what types of customization are allowed.
I have tried adding a .font(.title) modifier, for example, but that does not affect the text size. Ideally I'd like to change the button text to "Paste Photo" and get rid of the background, in addition to changing the font size, so that it matches how the app used to look with the custom button. How can I achieve this?
struct ButtonView: View {
private let supportedTypes: [UTType] =
[UTType("public.image")!]
var body: some View {
VStack {
PasteButton(supportedContentTypes: supportedTypes) { itemProviders in
// Handle paste
}
.padding(.bottom, 8)
Button
{
// Open photo library
} label: {
Image(systemName: "photo.on.rectangle.angled")
Text("Pick a Photo")
}
.font(.title)
}
}
}
From the Apple Document of PasteButton:-
The system provides a button appearance and label appropriate to the current environment. However, you can use view modifiers like buttonBorderShape(_:), labelStyle(_:), and tint(_:) to customize the button in some contexts.
PasteButton(supportedContentTypes: supportedTypes) { itemProviders in
// Handle paste
}
.labelStyle(.iconOnly) // To show icon only or .titleOnly to show title only
.tint(.red) // To change background color
.buttonBorderShape(.capsule) // To give capsule shape
So you cannot change the title of PasteButton but you can customize the button like above.
Note: You cannot clear the button background using .tint(.clear) this will make the background a little grey and I have also checked it doesn't even support custom LabelStyle.

Is there a SwiftUI version of the UIKit UIButton.ButtonType.close?

Right now, I am in the process of converting from UIKit to SwiftUI. In UIKit, there is a native Close, X-Styled Button - UIButton.ButtonType.close, like shown below:
I wanted to find the equivalent of this in SwiftUI, if built in to SwiftUI already. It is understandable if Apple hasn't gotten around to building/converting this yet. I see this also in the Apple Maps App, which I believe is built in SwiftUI, like shown below (on the bottom right corner of the panel):
If there isn't a built in button styling, how would one go about creating a View for this close button, which would adapt to light and dark mode? Thank you so much!
Edit: In UIKIt's Storyboard Editor, here is what I am looking for:
SwiftUI does not use concept of "button type", instead you can construct it by yourself, like
Button(action: {}) {
Image(systemName: "xmark.circle.fill") // << base !!
.resizable()
.frame(width: 32, height: 32) // << for demo
.foregroundColor(.gray)
}
*with any other modifiers as you wish
Xcode 13.4 / iOS 15.5
You can use the xmark.circle.fill symbol as the system image:
⚠️ Note: there is a difference between xmark.circle.fill and x.circle.fill
You can embed a UIKit UIButton with UIViewRepresentable.
struct CloseButton: UIViewRepresentable {
private let action: () -> Void
init(action: #escaping () -> Void) { self.action = action }
func makeUIView(context: Context) -> UIButton {
UIButton(type: .close, primaryAction: UIAction { _ in action() })
}
func updateUIView(_ uiView: UIButton, context: Context) {}
}
and then
CloseButton {
// dismiss()
}
Example:
SomeView()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
CloseButton {
// dismiss()
}
.disabled(isBusy) // SwiftUI modifiers work fine.
}
}
It is not currently possible to use system button types in SwiftUI including the close button. I’ve submitted FB10380135 to request this addition.
In the meantime, you could make a Button look like the system close button. Mike Stern, an Apple Design Evangelist, noted in an Ask Apple Q&A:
The modal close button uses the "xmark" SF Symbol at 15pt bold
(regular size symbol, not large or small variant) and a 30×30pt
circle. The hit area is likely bigger as we typically go for at target
size that's at least 44×44pt.
Also note the close button doesn’t change size as the dynamic type size increases/decreases. Do be sure to make its accessibility label “close” and add support for the large content viewer so people using accessibility text sizes can tap and hold the button to reveal a large xmark symbol with the word Close underneath.
I personally would recommend utilizing UIViewRepresentable to add a real close UIButton button though as opposed to trying to replicate it exactly - see the answer provided by MMP0.

What is wrong with the iOS Keyboard toolbar in SwiftUI

I am aware I asked a similar question before, but it seems like I have not understood the core concept of how to present a custom toolbar above a keyboard.
I successfully solved my problem on how to present one with a search field (SwiftUI 2.0: Custom keyboard elements).
Now I want to present a keyboard when a textfield within a detail view of a list is clicked, but again the keyboard toolbar does not show. Does anyone have an idea why?
VStack {
Text("Weight:")
TextField("0", text: $weight)
.keyboardType(.decimalPad)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
HStack {
Button(action: {
print("Set bodyweight")
},
label: {Text("Bodyweight")
})
Picker("", selection: $weightType) {
ForEach(weightSuffix.allCases, id: \.self) {
Text($0.rawValue)
}
}
.pickerStyle(.segmented)
}
}
}
}
.border(Color.red)
[EDIT]
After Asperi´s comment I Created a small git: https://gist.github.com/joni8a/bc021ef597cb6efa1ab0ca277d602478
Now it gets even weirder, if I attach the toolbar modifier to the list element I get the intended behavior, showing 1 button above the toolbar
If I append the toolbar modifier to the textfield inside the detail view I get the following result:
I think this is a weird behavior. It seems like I have not understood a core concept of SwiftUI. On the other hand if I can't attach the viewmodifer to the textfield itself, it is hard to uncouple the detail view from the list view ...

Swipe action image color SwiftUI

I'm trying to modify the color of the Image in the swipe action of a list in SwiftUI.
I have tried a few things:
Setting the image with rendering template and after that applied the foregroundColor modifier
Setting foregroundColor modifier directly to button
Tried to set a custom image
I searched a lot for this, and I didn't found anything, not only didn't found solution, I didn't found a NO, like "You can't do that", so this question is for me and anyone in the future trying to do the same thing and they being able to found a quick solution or a quick "Thats impossible".
A piece of my code, really simple:
List {
if viewModel.showSkeleton {
ForEach(0...4, id: \.self) { _ in
SkeletonTransactionRowView().listRowSeparator(.hidden)
}
} else {
ForEach(viewModel.transactions) { transaction in
TransactionRowView(transaction: transaction)
.swipeActions {
Button { viewModel.delete(transaction: transaction) } label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
.listRowSeparator(.hidden)
}
}
.listStyle(PlainListStyle())
NOTE: I know i can make a custom drag gesture and do all this stuff manually, but I want to know if it's possible to do it with the swipe action modifier.

How to disable SwiftUI's default behavior?

SwiftUI makes it very easy to build declarative UIs. However, sometimes they assume defaults that are not necessarily what we want.
Example:
When adding two buttons inside a list row, SwiftUI automatically makes the whole row touchable, and both button's actions are called on row tap. This is the default behavior, as demonstrated in their WWDC videos.
But I do not want this behavior. I want both buttons to work properly, and the row to not be tappable.
Question:
How can we tell our Guacamole expert (to use the WWDC reference) to stop assuming how I want my list (or any other behavior) to work?
Any help would be appreciated.
If the List's default behavior is not required, you could use a VStack:
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
print("foo")
}) {
Image(systemName: "photo")
}
Button(action: {
print("bar")
}) {
Image(systemName: "photo")
}
}
}
}
However if List is really required, then it could be customized by writing a custom ListStyle.
(Also take a look at this question: How to change ListStyle in List.)
It seems that SomethingStyle protocol is the way Apple wants developers to use to modify native SwiftUI elements/behavior. Another example would be ButtonStyle or TextFieldStyle, etc.
I am under the impression that Apple wants to enforce their style guidelines. Also to add onto #backslash-f answer you could also just use a for each instead of a list, this will give you a similar effect and allow much more customization.
struct doubleList: View {
var body: some View {
VStack{
ForEach(1 ..< 10) {index in
Button(action: {
print("foo")
}) {
Image(systemName: "photo")
}
}
}
}
}
Another option to try would be to wrap an UITableView into an UIViewRepresentable and try to enable buttons that way
It seems there might be another way around this by using tap gestures
Image(systemName: "photo")
.gesture(TapGesture().onEnded() {
print("action2")
})

Resources