2

I have a custom UISlider, but there is a side of the slider which is not roundedenter image description here

Here my custom view:

class PrimarySliderView: UISlider {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    override func trackRect(forBounds bounds: CGRect) -> CGRect {
        return CGRect(origin: bounds.origin, size: CGSize(width: bounds.width, height: 6))
    }

    func setup() {
        tintColor = .cornFlowerBlue
    }

How to round the right side also?

EDIT

The slider is cutted because my application is RTL, when I change to LTR this display correctly but not in RTL, how to resolve this problem?

In My AppDelegate I force RTL:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        UIView.appearance().semanticContentAttribute = .forceRightToLeft

        return true
    }
Mickael Belhassen
  • 2,970
  • 1
  • 25
  • 46
  • i tested your code ... it gives me perfect round corner slider – Jawad Ali Jan 28 '20 at 12:26
  • The 2 sides are rounded ?? – Mickael Belhassen Jan 28 '20 at 12:32
  • Yes as @jawadAli said I also tested just changed ```.cornFlowerBlue``` to ```.blue``` (Because I didn't define this extension to UIColor), & It is giving round shape at both end. – Afsar edrisy Jan 28 '20 at 12:32
  • Also add `@IBDesignable` at the top of class to assign it directly from storyboard. –  Jan 28 '20 at 12:33
  • Really strange thank you, I'll try to find out where the problem comes from – Mickael Belhassen Jan 28 '20 at 12:34
  • Even if I use `@IBInspectable var trackHeight: CGFloat = 2 override func trackRect(forBounds bounds: CGRect) -> CGRect { return CGRect(origin: bounds.origin, size: CGSize(width: bounds.width, height: trackHeight)) }` then also it is perfect –  Jan 28 '20 at 12:38
  • I understood why the slider is cutted because my application is RTL, when I change to LTR this display correctly but not in RTL, how to resolve this problem? – Mickael Belhassen Jan 29 '20 at 08:28

5 Answers5

2

Either you need to add rounded image for minimum and maximum track or you can create rounded view using colors

other workaround is

override func layoutSubviews() {
          super.layoutSubviews()

        self.layer.sublayers![1].cornerRadius = 10

    }

enter image description here

Jawad Ali
  • 13,556
  • 3
  • 32
  • 49
1

Here is a solution/workaround and it works with iOS 14 based on this answer. Also, it is tweaked to solve blurry edges.

if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
        setMinimumTrackImage(getImageWithColor(color: .blue, size: frame.size).resizableImage(withCapInsets: UIEdgeInsets(top: 3, left: 3, bottom: 3, right: 3), resizingMode: .stretch), for: .normal)
}

And here is the implementation of getImageWithColor:

private func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
    
    let layer: CALayer =  CALayer()
    layer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    layer.masksToBounds = true
    layer.cornerRadius = 3
    layer.backgroundColor = color.cgColor
    
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    layer.render(in: UIGraphicsGetCurrentContext()!)
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
}

NOTE: Don't forget to change cornerRadius and UIEdgeInsets to suit your needs.

Before applying the fix:

Before the fix

After applying the fix:

After the fix

FarouK
  • 740
  • 6
  • 17
1

iOS14+ version of Jawad's answer

final class RoundedSlider: UISlider {
    override func layoutSubviews() {
        super.layoutSubviews()

        if #available(iOS 14.0, *) {
            if let layers = layer.sublayers?.first?.sublayers, layers.count > 0, let layer = layers[1] {
                layer.cornerRadius = layer.bounds.height / 2
            }
        } else {
            if let layers = layer.sublayers, layers.count > 0, let layer = layers[1] {
                layer.cornerRadius = layer.bounds.height / 2
            }
        }
    }
}
Claus Jørgensen
  • 25,882
  • 9
  • 87
  • 150
  • 1
    This was the only thing that worked for me, except I'm not sure if @Claus tested it in a compiler because you have to move `let layer = layers[1]` outside of the if/else condition because `layers[1]` is not "un-wrappable" since it does not produce `nil`. I also changed `layers.count > 1` (instead of 0) to ensure it never errors when accessing the`layers[1]` index. – Eric Mar 29 '22 at 21:04
0

try this one

if(UIApplication.sharedApplication().userInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.RightToLeft)
{
   uiSlider.transform = CGAffineTransformMakeScale(-1.0, 1.0);
}
Chetan Hedamba
  • 293
  • 2
  • 9
0

For SwiftUI

The problem occurs, as mentioned in the question when forcing the environment to be RTL. like so .environment(\.layoutDirection, .rightToLeft)

I found a simple solution that works for me.

Just use a rotation effect like so: .rotationEffect(Angle(degrees: 180)) on the slider and it will look as if it's RTL.

This will just rotate the slider 180 degrees

Note

if you would like to add some padding make sure to put it after this modifier so it will make sense or else you will have to enter the opposite edge. (for example .bottom instead of .top)

This will apply the padding to the top

Slider(value: $progress)
    .rotationEffect(Angle(degrees: 180))
    .padding(.top, 20) // After rotating

This will apply the padding to the bottom

Slider(value: $progress)
    .padding(.top, 20) // before 
    .rotationEffect(Angle(degrees: 180))

Hope this helps :)

Ori
  • 567
  • 3
  • 9