16

I'm trying to implement a custom UISlider (subclass of UISlider), that behaves in a similar way to the native slider that has a glow around the thumb when it's highlighted. I'm having trouble with this, though.

The (effective) width of the highlighted thumb is obviously greater than the normal thumb (due to the glow). If I add transparent pixel padding to the normal state image, so that both images have the same width, the issue is that the normal state thumb never looks like it reaches the end of the track (it does reach the end, but the padding makes it appear otherwise).

If I use thumbRectForBounds: to offset the thumb to line up correctly with the beginning of the track, it only makes the issue worse at the other end.

If I keep both images only as wide as they need to be (ie, no padding on the normal state, and the two are not the same width), I've gotten closer. I can set the offset independently in thumbRectForBounds: based on the UIControl.state, but it seems like the there's some math magic going on behind the scenes. The only place where the UISlider thinks the images for both states line up perfectly is the exact center.

It seems like I may need to implement some math that determines the offset of the highlight state based on it relative position along the track. It would need an y-offset of zero at the center (the UISlider would center both images, which means they align). At the extremes, it would need a y-offset that completely makes up for the glow. And in between it would need to interpolate the y-offset.

This seems like a lot of work to recreate something the native slider does though, so perhaps I'm missing something obvious.

Update Current solution:

It's not entirely portable, since it makes some assumptions about the size of the glow being added. I'm adding the glow with an image rather than drawing it directly to the CALayer. I haven't tested how performant this is yet.

Essentially I create a layer that's a square whose dimensions are the height of the default state thumb image, center it over the thumb, and then draw the glow (only the glow, not a separate image of a thumb with a glow, as would be done with a hightlight state image) into the layer.

- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
  CGRect r = [super thumbRectForBounds:bounds trackRect:rect value:value];

  [thumbHighlightOverlayLayer removeFromSuperlayer];
  thumbHighlightOverlayLayer = [CALayer layer]; 

  int x = r.origin.x + (r.size.width / 2) - (r.size.height / 2);
  thumbHighlightOverlayLayer.frame = CGRectMake(x, r.origin.y, r.size.height, r.size.height);

  UIImage* thumbHighlightOverlayImage = [UIImage imageNamed:@"thumb-highlight"];
  thumbHighlightOverlayLayer.contents = (id)thumbHighlightOverlayImage.CGImage;

  switch (self.state) {
    case UIControlStateHighlighted:
      [[self layer] addSublayer:thumbHighlightOverlayLayer];
      break;
    default:
      break;
  }

  return r;
}
Farski
  • 1,670
  • 3
  • 15
  • 30
  • http://stackoverflow.com/questions/755510/customizing-uislider-look – VSN May 07 '12 at 12:50
  • The question is mainly in regards to the track image, not the thumb image. Writing my own slider may still be the only answer here, but since I'm trying to recreate native behavior, I'm still wondering if there's a way to avoid needing to do that. – Farski May 07 '12 at 13:29

3 Answers3

1

instead of adding glow effect in the image, it may be better to add it dynamically. you can use CALayer to add glow effect.

CALayer

Tejesh Alimilli
  • 1,782
  • 1
  • 21
  • 31
  • That will be quite hard, as UISlider doesn't seem to have any subviews or sublayers to which you could add a shadow. – Andreas Ley May 10 '12 at 12:41
  • Like Andreas says, this isn't completely straight forwards since there's no direct access to the thumb image's view, but I have come up with a solution that seems to be working. Thanks for the idea, Tejesh. – Farski May 10 '12 at 14:34
  • @farski Thank you. It would be nice if you could share the working solution. – Tejesh Alimilli May 17 '12 at 03:44
  • I added it to my original post last week. – Farski May 17 '12 at 13:41
0

You could roll your own slider, which isn't as complicated as it may sound but would still require a few hours of coding.

However, the simplest solution would be to just set custom track images as well:

  1. Create custom "minimum"/"maximum" track images with padding (transparent pixels) on the left/right side. The padding should be as large as your glow's radius minus the thumb radius (11pt).
  2. Apply images to your UISlider with setMinimumTrackImage:forState: and setMaximumTrackImage:forState:
  3. Increase your slider width by the size of the added padding
Andreas Ley
  • 9,109
  • 1
  • 47
  • 57
0

I came across a similar problem with a custom slider for one of my apps. Unlike the normal slider, the track is a static number line, so I don't need separate minimum and maximum track images. Yet, I needed an extra large hot spot for the thumb. So I made my thumb image with extra padding of transparent pixels, and I assigned transparent images to the track. The visible track is just a UIImageView behind the slider (actually one UIImageView behind a group of sliders). The edges of the slider extend well beyond the edges of the track image.

My suggestion is to make the visual construction of your intended slider by combining multiple views instead of knocking against the limitations of customizing a UISlider. If you can do without a changing track, you could duplicate my technique. Or you could manipulate the frame of a UIView or two to make custom tracks adjust whenever the slider changes value. If you need to use this same slider more than once in your app you can package up the group of views into a UIView subclass. Depending upon how complex your slider action gets, there may come a point where making a custom control would be the more prudent choice.

Mr. Berna
  • 10,525
  • 1
  • 39
  • 42