pragmatically scroll UIScrollView upon shake gesture - iphone

HI,
I have a view that has three UIScrollViews on the screen. I want to randomly scroll the UIScrollViews to different positions whenever a user shakes the iPhone, but I am unable to get it done.
I have implemented the following in my ViewController class to detect and handle the shake gesture, the 3 UIScrollViews also belong to the same class. The shake gesture is detected, but the UIScrollViews do not change. What am I doing Wrong??
i have tried both motionBegan and motionEnded.
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
int randomTag = arc4random() % [dirContents count];
CGRect nextImageView = [[scrollView1 viewWithTag:2] frame];
[scrollView1 scrollRectToVisible:nextImageView animated:YES];
randomTag = arc4random() % [dirContents count];
nextImageView = [[scrollView2 viewWithTag:4] frame];
[scrollView2 scrollRectToVisible:nextImageView animated:YES];
randomTag = arc4random() % [dirContents count];
nextImageView = [[scrollView3 viewWithTag:4] frame];
[scrollView3 scrollRectToVisible:nextImageView animated:YES];
NSLog(#"Shake Detected End");
}
}
Thank You

Have you tried using SetContentOffset instead of scrollRectToVisible yet?
if the images in your tableView are of equal height the offset per "Element" is always the same.
[scrollView3 setContentOffset:yourRandomOffsetInPixels animated:YES];
maybe this works.
also consider, that The Problem might be that your shake-detection Method runs on a separate Thread this would mean that you have to call your motionEnded Method on the mainthread like so:
[self performSelectorOnMainThread:#selector(motionEnded) withObject:nil waitUntilDone:NO];

Did you check your nextImageView variable to see if it was correct ?
Further more if you are trying will have the motion of a slot machine, I would recommend you to use UITableView instead of doing it by yourself with scrollView

Just one quick question. In your example code, you generate a random tag:
randomTag = arc4random() % [dirContents count];
but then you use a specific tag value (4 in this case)? I assume it still doesn't work when you use the randomTag value? and that you were just doing some testing?
nextImageView = [[scrollView2 viewWithTag:4] frame];

Related

IOS Scrollview Marquee style

I have a set of 4 images that i want to show in a UIScrollView. This scroll view contains ad banners. So they have to keep scrolling vertically down automatically similar to marquee effect - user does not need to touch the screen to scroll.This process should keep repeating.
If the user touches the screen, the scrolling should stop & when user re-touches the screen the scrolling should restart from where it had left.
There is as simpler way, I guess. UIImageView can help you. Follow these Steps :
1) Add 4 UIImageViews Vertically Down.
2) Create 4 Different Arrays for these ImageViews.
NSArray *frames1 = [NSArray arrayWithObjects:[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],nil];
NSArray *frames2 = [NSArray arrayWithObjects:[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],nil];
NSArray *frames3 = [NSArray arrayWithObjects:[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],nil];
NSArray *frames4 = [NSArray arrayWithObjects:[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],nil];
3) Provide these Arrays to all these different ImageViews.
yourImageView1.animationImages = frames1;
yourImageView2.animationImages = frames2;
yourImageView3.animationImages = frames3;
yourImageView4.animationImages = frames4;
4) You can add some Effects also...
// all frames will execute in 1.75 seconds
yourImageView.animationDuration = 1.75;
// repeat the annimation forever
yourImageView.animationRepeatCount = 0;
5) Simply startAnimating the ImageViews.
[yourImageView1 startAnimating];
[yourImageView2 startAnimating];
[yourImageView3 startAnimating];
[yourImageView4 startAnimating];
6) You can always use -touchesEnded method to start and stop Animation.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([[touches anyObject] view] == yourImageView1) {
//Here you can write the code to stop and start Animation of UIImageView
}
}
I think this will give you the effect what you want.
GoodLuck !!!
This is how I have done it finally :-) .
Call the below method from viewDidLoad
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(scrollTheScrollView) userInfo:nil repeats:YES];
Now implement the method
-(void)scrollTheScrollView{
static int i=0;
i++;
if(self.scrollView.contentOffset.y == 700.0)//This 700.0 will be different for you
i=0;
NSLog(#"Content offset in scrolling method is %#",self.scrollView);
[self.scrollView setContentOffset:CGPointMake(0, i*5)];}
I made an API for this, if you're interested.
https://github.com/dokun1/DOMarqueeLabel

MkAnnotation drop animation iOS 6

I try to animate my custom MkAnnotation, it works great on iOS 5 but not on iOS 6. Here is my didAddAnnotationViews method :
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews
{
NSTimeInterval delayInterval = 0;
for (MKAnnotationView *annView in annotationViews)
{
// Don't pin drop if annotation is user location
if ([annView.annotation isKindOfClass:[MKUserLocation class]]) {
continue;
}
// Check if current annotation is inside visible map rect, else go to next one
MKMapPoint point = MKMapPointForCoordinate(annView.annotation.coordinate);
if (!MKMapRectContainsPoint(self.mapView.visibleMapRect, point)) {
continue;
}
CGRect endFrame = annView.frame;
// Move annotation out of view
annView.frame = CGRectMake(annView.frame.origin.x, annView.frame.origin.y - self.view.frame.size.height, annView.frame.size.width, annView.frame.size.height);
[UIView animateWithDuration:1
delay:delayInterval
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
annView.frame = endFrame;
}
completion:^(BOOL finished){
if (isModal)
[self.mapView selectAnnotation:[[self.mapView annotations] objectAtIndex:0] animated:YES];
}];
delayInterval += 0.0625;
}
}
I made this method with some parts of code i found on internet. On iOS 5 the animation is perfect, but on iOS 6 pins are just appearing without any kind of animation. Setting the mapView delegate is the first thing I do on my viewDidLoad, and i've also tried to generate my annotations from viewDidAppear method , without success.
Any idea ?
Thanks.
EDIT : Solution found, I use the perform:withObject:afterDelay: method and it seems to work.
- (void)viewDidLoad
{
[super viewDidLoad];
mapView.delegate = self;
[self performSelector:#selector(addAnnotation) withObject:nil afterDelay:0.0];
}
- (void)addAnnotation
{
MapViewAnnotation *annotation = [[MapViewAnnotation alloc] initWithTitle:#"test" andCoordinate:CLLocationCoordinate2DMake(49.6, 6.2)];
[mapView addAnnotation:annotation];
}
I found a solution, look at the edit section.

Customizing UIPickerView

I have a requirement where UIPickerView should be customized. The picker view should look something like this:
The application which has customized the pickerView similarly is:
http://itunes.apple.com/us/app/convert-the-unit-calculator/id325758140?mt=8
I have tried removing the default pickerView selection bar by resetting the property showsSelectionIndicator of UIImagePicker and adding a overlay view. But the problem is, the overlay view should be transparent so that the wheel behind it is visible. But the other application somehow does it even though the selection bar is not transparent.
Any ideas on how to achieve this feat?
Thanks and Regards,
Raj
You're going to have to write your own from scratch on this one. UIPickerview isn't customizable. At. All. Sucks, but that's how it is. I'd start out creating a uitableview and layering a frame around it, and trying to mimic uipickerview.
I think you can print the subviews under the picker and modify them.
The UIPickerView create subviews after the data is first loaded.
With performSelecter:WithObject:afterDelay:, you can remove them or insert whatever you need.
- (void)viewDidLoad
{
[super viewDidLoad];
[self refreshClock];
[timePicker_ performSelector:#selector(leakSelf) withObject:nil afterDelay:0];
[self performSelector:#selector(setupTimePicker) withObject:nil afterDelay:0];
}
- (void)setupTimePicker {
[[timePicker_ subviewOfClass:NSClassFromString(#"_UIPickerViewTopFrame")] removeFromSuperview];
CGRect frame = timePicker_.frame;
frame.size.width += 20;
frame.origin.x -= 10;
timePicker_.frame = frame;
}
#implementation UIView(UIViewDebugTool)
- (void)leakSubview:(UIView*)subroot atDepth:(NSUInteger)dep {
DLog( #"trace sub view[%u] %#", dep, [subroot description] );
CALayer* layer = subroot.layer;
for( CALayer* l in layer.sublayers ) {
DLog( #"layer[%u] %#", dep, l );
}
for( UIView* v in subroot.subviews ) {
[self leakSubview:v atDepth:dep+1];
}
}
- (void)leakSelf {
NSUInteger dep = 0;
[self leakSubview: self atDepth:dep];
}
#end

how do i scroll through 100 photos in UIScrollView in IPhone

I'm trying to scroll through images being downloaded from a users online album (like in the facebook iphone app) since i can't load all images into memory, i'm loading 3 at a time (prev,current & next). then removing image(prev-1) & image (next +1) from the uiscroller subviews.
my logic works fine in the simulator but fails in the device with this error:
[CALayer retain]: message sent to deallocated instance
what could be the problem
below is my code sample
- (void)scrollViewDidEndDecelerating:(UIScrollView *)_scrollView
{
pageControlIsChangingPage = NO;
CGFloat pageWidth = _scrollView.frame.size.width;
int page = floor((_scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
if (page>1 && page<=(pageControl.numberOfPages-3)) {
[self removeThisView:(page-2)];
[self removeThisView:(page+2)];
}
if (page>0) {
NSLog(#"<< PREVIOUS");
[self showPhoto:(page-1)];
}
[self showPhoto:page];
if (page<(pageControl.numberOfPages-1)) {
//NSLog(#"NEXT >>");
[self showPhoto:page+1];
NSLog(#"FINISHED LOADING NEXT >>");
}
}
-(void) showPhoto:(NSInteger)index
{
CGFloat cx = scrollView.frame.size.width*index;
CGFloat cy = 40;
CGRect rect=CGRectMake( 0, 0,320, 480);
rect.origin.x = cx;
rect.origin.y = cy;
AsyncImageView* asyncImage = [[AsyncImageView alloc] initWithFrame:rect];
asyncImage.tag = 999;
NSURL *url = [NSURL URLWithString:[pics objectAtIndex:index]];
[asyncImage loadImageFromURL:url place:CGRectMake(150, 190, 30, 30) member:memberid isSlide:#"Yes" picId:[picsIds objectAtIndex:index]];
[scrollView addSubview:asyncImage];
[asyncImage release];
}
- (void) removeThisView:(NSInteger)index
{
if (index<[[scrollView subviews] count] && [[scrollView subviews] objectAtIndex:index]!=nil) {
if ([[[scrollView subviews] objectAtIndex:index] isKindOfClass:[AsyncImageView class]] || [[[scrollView subviews] objectAtIndex:index] isKindOfClass:[UIImageView class]]) {
[[[scrollView subviews] objectAtIndex:index] removeFromSuperview];
}
}
}
For the record it works OK in the simulator, but not the iphone device itself.
any ideas will be appreciated.
cheers,
fred.
I guess your AsyncImageView class uses asynchronous connections to load the image (which is a good thing), so that means that when you add it as a subview of your scroll view, it might be "removedFromSuperview" before the image loads completely. Check in the implementation of your class if the delegates are properly nil'd when it's been dealloc'd, and also that any connections are cancelled and so on.
Pay attention to the fact that a class should never retain its delegate, an "assign" kind of property is perfect for these situations.
Some tips:
Instead of alloc/init'ing and release'ing so many instances while you scroll, try to keep three ivars in your class, and just change the "frame" property of the instances as you scroll. You will avoid lots of allocation and releases if the user scrolls fast, and that's a good thing on an iPhone.
Avoid direct ivar accesses like that in your code; use properties, there are many advantages to them beyond the simple "correctness" thing (KVO being one of them).

Captured which subview is touched in UiScrollView but cannot access it in another function.

I have UIScrollView which has subviews (pictures) added do it. each time a user touches the a picture in the scrollview, it toggles a checkmark ontop of it.
NSMutableIndexSet *picturesArray; <- declared in .h
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event {
if (!self.dragging) {
[self.nextResponder touchesEnded: touches withEvent:event];
NSLog(#"Touch down");
for (UITouch *touch in touches) {
for (int i = 1; i <= [self subviews].count; i++)
{
if(CGRectContainsPoint([[self viewWithTag:i]frame], [touch locationInView:self])){
NSLog(#"touched %d th view",i);
NSArray *subviews = [[self viewWithTag:i] subviews];
UIImageView *view = nil;
view = [subviews objectAtIndex:0];
if(view.hidden){
// add the index
[picturesArray addIndex:i];
view.hidden = NO; //check mark is shown
}else{
[picturesArray removeIndex:i];
view.hidden = YES; //check mark is not shown
}
// UIImageWriteToSavedPhotosAlbum([(UIImageView *)[self viewWithTag:i]image], nil, nil, nil); <- WORKS IF CALLED
}
}
}
}
Question 1: is this the best way of doing this? It seems like using a for (int i = 1; i <= [self subviews].count; i++) is pretty slow. I basically need to capture which subview was touched. I havent figured this out other than going through EACH subview
savePhotos is called and basically searches through which of the pictures was touched and saves them to the Photo Album. However the call to UIImageWriteToSavedPhotosAlbum fails. This is in the same file as TouchesEnded. But when called in TouchesEnded, it works.
(IBAction) savePhotos: (id) sender{
NSLog(#"The index set is %#",picturesArray );
const NSUInteger arrayCount = picturesArray.count;
NSUInteger *theIndexBuffer = (NSUInteger *)calloc(picturesArray.count, sizeof(NSUInteger));
UIImageWriteToSavedPhotosAlbum([(UIImageView *)[self viewWithTag:0]image], nil, nil, nil);
[picturesArray getIndexes:theIndexBuffer maxCount:arrayCount inIndexRange:nil];
for(int i = 0; i < arrayCount; i ++){
NSLog(#"Element is %d",theIndexBuffer[i]);
UIImageWriteToSavedPhotosAlbum([(UIImageView *)[self viewWithTag:i]image], nil, nil, nil); <- THIS CRASHES
}
}
Question 2: Why is it that UIImageWriteToSavedPhotosAlbum is failing ?
1) instead of using UIImageView, implement a child of UIImageView. Then try listening for touches on the individual subviews, that should solve your O(n) problem
2) something is probably getting auto-released, double check your reference counting is correct
try UIView* targetView = [self hitTest:location withEvent:nil];