1

Below is what I want to make

enter image description here

What I have is ScrollView001 & ScrollView002. Layout is as below.

- View
  - ScrollView001
  - ScrollView002

ScrollView002, takes full screen so ScrollView001 is obviously below ScrollView002.

I am doing this for some effect i.e. when I scroll ScrollView002, I want to scroll ScrollView001 but with lower speed, which I have done successfully but the problem is when I try to click on Car for Gallery, its not clicking. If I hide ScrollView002 simply, gallery is working as usual.

Any idea how can I make underlying view as clickable?

For Gallery, what I have used is UICollectionView.


Edit 1

After scrolling, I want somewhat like below.

enter image description here

Fahim Parkar
  • 30,974
  • 45
  • 160
  • 276
  • I am not sure about your requirement or effect how you need, but my suggestion is to put `UICollectionView` (ScrollView001) inside `UITableViewCell` (UITableView is ScrollView002). – SriKanth May 18 '16 at 06:56
  • why scrollview1 is below scrollview2 ? – Ketan Parmar May 18 '16 at 06:58
  • @Lion : I already said in my question **when I scroll ScrollView002, I want to scroll ScrollView001 but with lower speed, which I have done successfully** – Fahim Parkar May 18 '16 at 07:01
  • why you are not taking scrollview1 above scrollview2? you can achieve your goal with this scenario also – Ketan Parmar May 18 '16 at 07:04
  • @Lion : then what I have in scrollview2 will go below car gallery which I don't want... its like i want to hide gallery on scrolling up... – Fahim Parkar May 18 '16 at 07:05
  • @Lion : check my update... might clear you... – Fahim Parkar May 18 '16 at 07:07
  • @FahimParkar check this might be help http://stackoverflow.com/questions/11485100/allow-uiscrollview-and-its-subviews-to-both-respond-to-a-touch – Badal Shah May 18 '16 at 07:23

3 Answers3

1

Fahim Parkar,

Not sure this is what you want or have I understood you wrong :) I believe you can makeuse of hitTest: to solve it :)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    if (hitView && CGRectContainsPoint(ScrollView001.frame, point)) {
        return ScrollView001;
    }
    return hitView;
}

How it Works

Whenever you tap on screen scorllView002 covering whole screen captures the touch :) Hence hitTest of ScrollView002 gets called :) If by anyway you can access the ScrollView001 (that should be easy as they are in same ViewController) you can verify if the touch point is inside ScrollView001, If yes you can return the touchView as ScrollView001.

As a result ScrollView001 will get touch rather then ScrollView002 :)

EDIT

As you have mentioned you did try my method and it still din work for you :) I decided to give a shot myself and realized it works absolutely fine. Please have a look :)

enter image description here

I believe this is what your situation :) I have added two scrollViews on top of each other. Small scrollView (myScrollView) being below the bigger fullscreen scrollView (TestScrollView).

Now when I tap on the screen there is no way myScrollView getting touch :) But thats what we need so here is what I did :)

I created a subclass of ScrollView called TestScrollView and set the same for the top scrollView :)

TestScrollView.swift

import UIKit

class TestScrollView: UIScrollView {
    var scrollViewBehind : UIView!
    /*
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        // Drawing code
    }
    */

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, withEvent: event)

        if hitView != nil && CGRectContainsPoint(scrollViewBehind.frame,point) {
            return scrollViewBehind
        }
        return hitView;
    }
}

I have created a variable called scrollViewBehind to hold the reference of smallerScrollView which is behind it in storyboard (I am very much aware that holding a reference like this to view behind is not a great design :) But I believe it is good enough to explain the logic )

This is my ViewController code :)

import UIKit

class ViewController: UIViewController,UIGestureRecognizerDelegate {

    @IBOutlet var myScrollView: UIScrollView!
    @IBOutlet var testScrollView: TestScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        testScrollView.scrollViewBehind = myScrollView
        let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap"))
        tap.delegate = self
        myScrollView .addGestureRecognizer(tap)
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    func handleTap(){
        print("Me tapped")
    }
}

EDIT

As per our discussion, I realized that you have a collectionView as a subView on smaller ScrollView which itself is behind the full screen scrollView :)

I have kept the answer above as it is in Swift and it also explains how to handle hitTest to handover the control to scrollView below from the scrollView above. Though it does not completely solve your question, might give a solution enough to somebody else :)

Now as per your question, when user taps, the scrollView above captures the touch and the touch should be forwarded to collectionView on top of smaller scrollView :)

So following the same approach explained above :) I created a subclass of ScrollView lets call TestScrollView (Answer is in objective C as per your requirement)

TestScrollView.h

#import <UIKit/UIKit.h>

@interface TestScrollView : UIScrollView
@property (nonatomic,strong) UICollectionView *collectionViewBehind;
@property (nonatomic,strong) UIScrollView *scrollViewBehind;

@end

TestScrollView.m

#import "TestScrollView.h"

@implementation TestScrollView

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *hitView = [super hitTest:point withEvent:event];

    CGPoint myPoint = [self convertPoint:point toView:self.scrollViewBehind];
    if (hitView != nil && CGRectContainsPoint(self.scrollViewBehind.frame,point)) {
        return self.collectionViewBehind;
    }
    return hitView;
}
@end

If you notice properly TestScrollView has two properties namely collectionViewBehind and scrollViewBehind this is to hold the reference of below scrollView and collectionView :)

What hitTest does is already explained above. Though, all it does is it checks the touch point if touch point is inside scrollView it returns the collectionView. What taht does is it transfers the touch event to collectionView.

Now go to storyboard select the topScrollView and set its class as TestScrollView.

In ViewController which loads these scrollViews add,

#import "ViewController.h"
#import "TestScrollView.h"

@interface ViewController ()<UICollectionViewDelegate,UICollectionViewDataSource,UIGestureRecognizerDelegate>
@property (strong, nonatomic) IBOutlet TestScrollView *testScrollView;
@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.testScrollView.collectionViewBehind = self.collectionView;
    self.testScrollView.scrollViewBehind = self.scrollView;

    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"test"];


    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tapGesture.numberOfTapsRequired = 1;
    tapGesture.delegate = self;
    [self.collectionView addGestureRecognizer:tapGesture];


    // Do any additional setup after loading the view, typically from a nib.
}

Yes, If you have noticed you noticed it right I am adding a TapGesture recognizer to UICollectionView. Though we managed to handover the touch to UICollectionView we noticed that didSelectItemAtIndexPath was never triggered. So this is a small work around.

Though adding GestureRecognizer to collectionView is not much advisable here in this case it is still ok as we are only overriding single tap leaving all other gestures unaltered.

So now whenever user taps the scrollView above gestureRecognizer of UICollectionView triggers and calls our selector :) Now all you have to do is to figure out which cell was tapped and select it programmatically :)

-(void)handleTap:(UIGestureRecognizer *)recognizer {
    [self collectionView:self.collectionView didSelectItemAtIndexPath:[self.collectionView indexPathForItemAtPoint:[recognizer locationInView:self.collectionView]]];
}

Finally enjoy the control at

-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"Hi indexPath tapped is %@",indexPath);
}

Finally here is a link to the project : https://github.com/sandeeplearner/UnderlayingClickableViews

Sandeep Bhandari
  • 19,999
  • 5
  • 45
  • 78
  • @fahim-parkar : Bro did you try my answer :) It should work :) I have a working code in one of my app in app store :) Not on scrollView though :) – Sandeep Bhandari May 18 '16 at 07:09
  • to be honest that would have worked if scrollview1 don't exists and scrollview2 dont scroll over scrollview1... – Fahim Parkar May 18 '16 at 07:10
  • @fahim-parkar : Bro I just tested my code again :) It worked absolutely fine :) Lemme post ma whole code check it :) – Sandeep Bhandari May 18 '16 at 08:01
  • I think I know why it is not working.. bcz UIScrollView don't respond to hitTest.. I think so... – Fahim Parkar May 18 '16 at 08:05
  • @fahim-parkar : I afraid Ill disagree with that bro :) I just checked and am posting the whole code as well :) And yes scrollView responds to hitTest :) Any object having UIView as parent will respond to hitTest :) – Sandeep Bhandari May 18 '16 at 08:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/112240/discussion-between-fahim-parkar-and-sandeep-bhandari). – Fahim Parkar May 18 '16 at 08:09
  • [bhai are you there for this??](http://stackoverflow.com/questions/37373858/whatsapp-share-is-not-working-same-code-is-working-in-another-project) – Fahim Parkar May 22 '16 at 11:48
  • @fahim-parkar : Sorry bhai abhi dekha :) Waise bhi whatsapp share pe kabhi kaam nai kiya so kuch jyaada kaam nai aapaata :) Dekh ke achha laga ki aapne kisika to answer accept kiya he so your question must have been solved :) – Sandeep Bhandari May 22 '16 at 19:34
0

Try this Take button as background for your car then imageview for car so that it can be clickable.. i think there i is no need to make view cickable

user3306145
  • 76
  • 2
  • 12
0

You should add tapgesturerecognizer on upper scrollview, then you can handle tap something like,

- (void)handleTap:(UITapGestureRecognizer *)recognizer {

// First get the tap gesture recognizers's location in the entire 
// view's window
CGPoint tapPoint = [recognizer locationInView:self.view];

// Then see if it falls within one of your below images' frames
for (UIImageView* image in relevantImages) {

    // If the image's coordinate system isn't already equivalent to
    // self.view, convert it so it has the same coordinate system
    // as the tap.
    CGRect imageFrameInSuperview = [image.superview convertRect:image toView:self.view]

    // If the tap in fact lies inside the image bounds, 
    // perform the appropriate action.
    if (CGRectContainsPoint(imageFrameInSuperview, tapPoint)) {
        // Perhaps call a method here to react to the image tap
        [self reactToImageTap:image];
        break;
    }
  }
}

You can consider your car or instead of imageview in your case.

Hope this will help :)

Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75