Changing the thumb image of the slider conditionally in SwiftUI - swiftui

I've been looking ways to change my thumb image of the slider conditionally for every value change. Since I was not able to find ways to locate the thumb, I used customised slider, using this article. It works perfectly fine, however, I'm not sure how to change the thumb image conditionally.
Please help.
Pretty new to SwiftUI.

You can use slider method,
setThumbImage(image, for: .normal)
setThumbImage(image, for: . highlighted)
And as per your requirement if you want to change image on value change you need to add your code like (code is from your attached link sample),
#objc func valueChanged(_ sender: UISlider) {
self.value.wrappedValue = Double(sender.value)
let arra: [UIImage?] = [UIImage(systemName: "heart"), UIImage(systemName: "pencil"), UIImage(systemName: "trash")]
sender.setThumbImage(arra.randomElement() as? UIImage, for: .normal)
sender.setThumbImage(arra.randomElement() as? UIImage, for: .highlighted)
}

Related

Swiftui list: trigger an action on tap and mimic basic select then auto-unselect

I'm starting to learn swiftui and I've run into a problem that is both very basic and easily solvable in UIKit; but after spending days searching the internet and watching WWDC videos I've found no native solution.
The premise is simple: I have an array of songs I want to display in a list; when a user taps on a song view it should highlight the view on press, unhighlight after release, and then play the song (ie trigger an action). Sounds simple right?
Here's what I tried and spent way too much time on:
Using List(selection) + .onEvent(changed): I end up with a UUID (because i've only gotten selection to work with a UUID) that I then have to check against an array of songs to match AND the cell won't unhighlight/select itself; even when I try to manually set the State variable to nil or another generated UUID.
Using .onTap (either on or in the cell): I have to tap on the text of the cell to trigger onTap so I get a lot of taps that just don't work (because I have lots of white space in the cell). I also don't get a nice UI color change on press/release.
So after spending hours trying many different things I've finally come up with a solution and I basically wanted to create an account and share it to hopefully help other developers in my position. Because this so very annoyed me that something so basic took so much effort and time to do.
In the end the best solution I came up with was this:
Using ZStack and an empty button:
edit: I found I need to include and hide the content otherwise the button doesn't grow to fill the space (seems in lists it does for some reason). Though not sure what the hit on performance is of rendering the content twice when hiding it. Maybe a GeometryReader would work better?
struct SelectionView: ViewModifier {
let onSelect: () -> Void
func body(content: Content) -> some View {
ZStack (alignment: .leading) {
Button {
onSelect()
} label: {
content
.hidden()
}
content
}
}
}
extension View {
func onSelection(_ selection: #escaping () -> Void) -> some View {
self.modifier(SelectionView(onSelect: selection))
}
}
then to use it:
SongCell(song: song)
.onSelection {
// Do whatever action you want
}
No messing around with list selection, no weird tap hit boxes, and get the press/release color change. Basically put an empty button in a ZStack and trigger off it's action. Could possibly cause tap/touch issues with more complicated cells (?) but it does exactly what I need it to do for my basic app. I'm just not sure why it took so much effort and why apple doesn't support such a basic use case by default? If I've overlooked something native please do inform me. Thanks.
I got the basic idea what you are trying to do. I'm Going to show simple example. Maybe using this you will be able to find proper solution.
First let's create a color : -
#State var colorToShow : Color = Color.blue
Now in body we have our ZStack or Your cell that we want to deal with : -
ZStack{
colorToShow
}.frame(width: 50, height: 50).padding()
.onLongPressGesture(minimumDuration: 3) {
print("Process Complete")
colorToShow = .green
} onPressingChanged: { pressing in
if pressing {
print("Pressing")
colorToShow = .red
} else {
print("Pressing Released")
colorToShow = .blue
}
}
Here we are using .onLongPressGesture. You can set minimum duration on which you want to perform action. Now on process completion You set what you want to do. OnPressingChange give you a bool value that changes according to user is pressing that button or not. Show color change(Highlight) or do action while bool value is true. When user release button do action or unhighlight since bool value turns false.
Hope you find it useful.

SwiftUI: Custom view is un-hittable in XCTest

During Xcode UI test, I found my custom view's (MenuViewButton) is un-hittable in a test, I could found it but cannot touch it. In debug , when I po isHittable in console, it returns false. However I'm not sure if this is the correct behavior.
Per this thread XCUIElement exists, but is not hittable said, isHittable is default false for custom view element, and default true for UIKit standard view. But I don't know if it is the same behavior in SwiftUI.
Since the way someView.isAccessibilityElement = true is not possible in SwiftUI. My question is how could I let my custom view became hittable? Then it could be tapped in a test.
private var aView: some View {
MenuViewButton(
image: Image("an image name"),
text: Text("a string")
)
.accessibility(identifier: "xxx name")
}
I also use force tap with coordinate in case tap() is not working, but to give a offset of normalizedOffset didn't fix the problem in all places, it means some un-hittable element could be tapped, that is great but some others still not.
So may I know where normalizedOffset is start, from the middle of frame to give the offset or the top left?
func forceTapElement(timeout: TimeInterval) {
if !self.waitForExistence(timeout: timeout) {
return
}
if self.isHittable {
self.tap()
} else {
let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.0))
coordinate.tap()
}
}
Add https://github.com/devexperts/screenobject as a dependency
Use .tapUnhittable() instead of .tap() for this particular view. It gets the coordinates and taps using them
I have the same situation and am looking for answers. What has worked for me was to use:
let coordinate = element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
Still this seems like workaround for a real problem. One other strange thing is that i do not have this problem on iOS15.0 simulator, only for later versions. Currently trying with iOS15.2
One more thing I've tried is to add
.accessibilityElement(children: .combine)
and specificly telling it's a button with
.accessibility(addTraits: .isButton)
But this doesn't solve the problem.
Seems that isAccessibilityElement would be an answer here, but SwiftUI doesn't seem to have such.

Swift 3 pass button/gesture recognizer action to other function using selector

I am having a problem with Swift 3 action selectors - or, I can say, with gesture recognizers.
Suppose I am in a table view where I am having dynamic data through a table cell. I have one button or gesture recogniser in the table view and I want to send data from that button or gesture to a different function.
Please try the below function, Hope it will work for you!!
func whereYouWant(){
let button = UIButton()
button.accessibilityElements = [data1,data2]//Here you can pass any thing in this array.
button.addTarget(self, action: #selector(testFunction), for: .touchUpInside)
}
//Here you can call this function through selector....
func testFunction(myButton:UIButton){
let firstData = myButton.accessibilityElements?[0] as? //Here you can do type cast According to you
let secondData = myButton.accessibilityElements?[1] as? //Here you can do type Cast According to you.
}

displaying image in a toolbar in swift 3

i am trying to create a toolbar with buttons. and the button i want to have is an image rather title. The current non working code is:
let imageName = "yourImage.png"
self.myUIBarButtonBack = UIBarButtonItem(image: UIImage(named: imageName), style:.plain, target: self, action: #selector(onClickBarButton))
I have 2 questions:
1. where should i place the yourImage.png in my application
2. is this code sufficient to render image or i need to do things like putting it into imageView component and make it visible etc. ?
The best approach is to add images in xcassets. This is the best way you can organize images. The concept of App slicing applies here.
You don't need to put the image in image view in the case of bar button item.
Try changing the rendring option as Original Image instead of Default.
One way is create custom button and assign to toolbar like navigationbar
self.navigationItem.hidesBackButton = true
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "back_icon"), for: UIControlState.normal)
button.addTarget(self, action:#selector(onClickBackBarItem), for: UIControlEvents.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width: 25, height: 25)
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton

Make Link Detected by NSDataDetector clickable in Swift 3 OS X

I thought that if the person posted a link within a description inside a textfield that I could make that link clickable. So, I first thought I would need to detect the link (which I have done) then make it clickable...I have tried several different things but still nothing. I have the link printing to my console--I just need the link to be clickable within the textfield.
You can add a UITapGestureRecognizer to your label:
let label = UILabel()
let clickGesture = UITapGestureRecognizer(target: self, action: #selector(click(_:)))
label.gestureRecognizers = [clickGesture]
func click(_ gestureRecognizer: UITapGestureRecognizer) {
//do stuff on label click
}