iKy1e | iOS Developer

How to put UISlider’s in UIScrollView’s

It’s a fairly simple request you’d imagine. Simply put a slider inside some content which scrolls.

As this is on iOS that content is obviously inside a UIScrollView.

 

The Problem

The main problem with the above is that they don’t like each other. If you place a standard UISlider inside a normal UIScrollView the scroll view delays the touches for a fraction of a second to see if you are scrolling. Here is the big problem, you swipe sideways to scroll and you, want to, swipe sideways on the sliders thumb to quickly change it’s value.

As a result you have to tap, pause and then drag the slider in order to use it. Manageable but hardly smooth or ideal to use.

Of course there is a very simple way to avoid this.

scrollView.delaysContentTouches = NO;

Simple? Well, at least for me on iOS 5, a little too simple. This caused almost all objects to prevent my scrollView from scrolling, buttons, sliders, even an image view seemed to block it. So we filter for the slider?

If we look at the UIScrollView documentation we see a few little things that allow us to control the delays and cancelling touches. Creating a custom subclass gives us a little more power, but still not enough.

What do we do with our new subclass then? Well, we need to control when to allow touches to behave normally and when we should scroll.

@interface KHCustomScrollView : UIScrollView
@property (nonatomic, assign) BOOL ignoreSliders;
@end


@implementation KHCustomScrollView
@synthesize ignoreSliders = _ignoreSliders;

-(BOOL)touchesShouldCancelInContentView:(UIView *)view{
    if (self.ignoreSliders) {
		if ([view isKindOfClass:[UISlider class]]) {
			return NO;
		}
		else {
			return YES;
		}
	}
	
	return [super touchesShouldCancelInContentView:view];
}
@end


// Somewhere else...
scrollView.canCancelContentTouches = YES;
scrollView.delaysContentTouches = NO;
scrollView.ignoreSliders = YES;

What we do here is tell it we want the scrollView to let all touches to start normally, but we want it to be able to cancel them if it wants to scroll. Instead of the normal behavour of delaying them until its decided whether or not to scroll it now cancels touches when it wants to. Then we have our scrollView say it can cancel touches and start scrolling for everything, other than sliders.

Now can you guess the problem here?

If we touch anywhere on the slider it will not scroll, however the slider only responds over the thumb image that indicates the value selected. But we can fix this too, however we have to work with the sliders themselves :(, although luckily we don’t need to subclass them.

// Somewhere...

-(void)monitorSlider:(UISlider*)slider{
    [slider addTarget:self action:@selector(willSlide:) forControlEvents:UIControlEventTouchDown];
	[slider addTarget:self action:@selector(didSlide:) forControlEvents:UIControlEventTouchUpInside];
	[slider addTarget:self action:@selector(didSlide:) forControlEvents:UIControlEventTouchUpOutside];
	[slider addTarget:self action:@selector(didSlide:) forControlEvents:UIControlEventTouchCancel];
}
-(void)willSlide:(id)sender{
	scrollView.scrollEnabled = NO;
}
-(void)didSlide:(id)sender{
	scrollView.scrollEnabled = YES;
}


// Someone else...
[self monitorSlider:slider];



// In our scrollView subclass we made earilier
if ([view isKindOfClass:[UISlider class]] && !self.scrollEnabled) {
    return NO;
}

By the way that `scrollView.scrollEnabled` is only state storage. It could have been written scrollView.tag = 42, if (scrollView.tag == 42) …

So now we allow touches to reach things inside the scrollView, we know when the slider was touched on its thumb image, and we can control which touches are canceled and which are allowed to prevent us from scrolling.

 

 

And finially, here’s an example project on Github to show you an example for each stage of this article. Hope someone finds this useful as I couldn’t find anything on the subject.


Tutorial: Cool UIScrollView Effects

While I was working on the “Cardflow” interface mode for CardSwitcher I had to work out get custom page widths and custom effects as you scroll through the cards, this is actually quite simple but I haven’t seen many posts on customising a UIScrollView. Most of the tips are spread across lots of different stackoverflow questions.

  • Problems with custom scrollview’s

To start with (actually while I was working on the first version of CardSwitcher) I looked into iCarousel, which has quite a lot of cool effects and a tableView style data source and delegate protocols. However as the first version of CardSwitcher was actually just a standard WebOS, or Safari tab’s style, linear layout that highlighted a problem with iCarousel (which joehewitt also noticed with iScroll), caused by the fact it doesn’t customize a UIScrollView but instead uses UIGestureRecognizers to do the scrolling itself (like UIScrollView itself).

However because of this the scrolling just feel wrong, the physics are off by a little bit. The bounce as you scroll of the edge, flicking and then how many pages it scrolls past before stopping and the paging effect all just feels off slightly.

Read More


Tutorial: Customise UIWebView Menu

In safari if you tap and hold down over an image you get a list of options which includes things not in the UIWebView’s standard popup menu, like saving the image.

Now the UIWebView doesn’t provide a way to customise it’s menus by default so we have to turn to some custom code and javascript to override it’s default behaviour.

Read More


Tutorial: Complete iOS Game (Part 4)

In the previous parts of this tutorial we covered making the menu and making the basic game in Sparrow-Framework. Now we are going to refine the game and actually use the OpenFeint, that we set up at the beginning.

Some of the refinements we’ll be making.

  • Adding scoring;
  • Adding a pause menu;
  • Improving the AI slightly;
  • Add some settings





First, before we begin, a few notes about iPad compatibility. Although I’ve just been mentioning the AppDelegate_iPhone files, rather then both _iPhone and _iPad files, we have been using the window’s size rather then hard coding values. So you should just be able to copy and paste the code from below applicationDidFinishLaunching, with only 2 modifications.

  1. The OpenFeint initialisation code on the iPad needs to be done after [window makeKeyAndVisible];
  2. The game needs to initialised with “window.frame.size.width” and “window.frame.size.height” rather then ‘320’ and ‘480’;


First we are going to create a few new classes.

Read More


Tutorial: Complete iOS Game (Part 3)

For the third part of my Complete iOS Game Tutorial series we will, finally, be using Sparrow-Framework and creating the game itself, which includes some basic collision detection and AI.






Now last time we made the base for the app, the main menu and the cool animations into the game. But we didn’t actually make anything in that game view did we.

At the moment you’ve got a cool main menu that then shows you a red square and leaves you stuck in the game view without being able to get back to the main menu.


So the next thing to do is make the game part of the app.

Now this is a universal app and I’ve chosen to only use the single Game class rather then a separate one for iPhone and iPad which means we will be doing everything, as much as possible, relative to the stages size rather then giving it absolute values.

Read More


12
To Tumblr, Love PixelUnion