简体   繁体   English

通过UIScrollView使基础视图可单击

[英]making underlying views clickable through UIScrollView

Below is what I want to make 以下是我想做的

在此处输入图片说明

What I have is ScrollView001 & ScrollView002 . 我所拥有的是ScrollView001ScrollView002 Layout is as below. 布局如下。

- View
  - ScrollView001
  - ScrollView002

ScrollView002 , takes full screen so ScrollView001 is obviously below ScrollView002 . ScrollView002 ,需要全屏显示,因此ScrollView001显然在ScrollView002下面。

I am doing this for some effect ie 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. 我这样做是为了达到某种效果,即当我滚动ScrollView002 ,我想以较低的速度滚动ScrollView001 ,这已经成功完成了,但问题是当我尝试单击Car for Gallery时,它没有单击。 If I hide ScrollView002 simply, gallery is working as usual. 如果我只是简单地隐藏ScrollView002 ,则图库将照常工作。

Any idea how can I make underlying view as clickable? 知道如何使基础视图可点击吗?

For Gallery, what I have used is UICollectionView. 对于Gallery,我使用的是UICollectionView。


Edit 1 编辑1

After scrolling, I want somewhat like below. 滚动之后,我想要下面的内容。

在此处输入图片说明

Fahim Parkar, 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 :) 不知道这是您想要的还是我误会您的意思:)我相信您可以利用hitTest:来解决它:)

- (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. 每当您点击屏幕时,覆盖整个屏幕的scorllView002都会捕获触摸:)因此,将调用ScrollView002的hitTest :)如果无论如何您都可以访问ScrollView001(这应该很容易,因为它们在同一ViewController中),您可以验证触摸点是否在ScrollView001中,如果是,则可以将touchView作为ScrollView001返回。

As a result ScrollView001 will get touch rather then ScrollView002 :) 结果是ScrollView001将得到触摸,而不是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 :) 请看一看 :)

在此处输入图片说明

I believe this is what your situation :) I have added two scrollViews on top of each other. 我相信这就是您的情况:)我在彼此之上添加了两个scrollViews。 Small scrollView (myScrollView) being below the bigger fullscreen scrollView (TestScrollView). 小的scrollView(myScrollView)在较大的全屏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 :) 现在,当我点击屏幕时,myScrollView无法触摸:)但这就是我们需要的,所以这就是我所做的:)

I created a subclass of ScrollView called TestScrollView and set the same for the top scrollView :) 我创建了一个名为TestScrollView的ScrollView子类,并为顶部的scrollView设置了它:)

TestScrollView.swift 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 ) 我创建了一个名为scrollViewBehind的变量,以保存情节提要中位于它后面的smallerScrollView的引用(我非常清楚,拥有像这样的引用以在后面查看不是一个很好的设计:),但是我相信它足以解释逻辑)

This is my ViewController code :) 这是我的ViewController代码:)

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 :) 根据我们的讨论,我意识到您有一个collectionView作为较小的ScrollView上的子视图,它本身位于全屏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. 我保留了上面的答案,就像在Swift中一样,它还解释了如何处理hitTest将控件从上方的scrollView移交给下方的scrollView。 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 :) 现在根据您的问题,当用户点击时,上面的scrollView会捕获触摸,并且应该将触摸转发到较小的scrollView顶部的collectionView :)

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) 因此,按照上述相同的方法进行操作:)我创建了ScrollView的子类,让其调用TestScrollView(根据您的要求,答案在目标C中)

TestScrollView.h TestScrollView.h

#import <UIKit/UIKit.h>

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

@end

TestScrollView.m 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 :) 如果您正确地注意到TestScrollView具有两个属性,即collectionViewBehind和scrollViewBehind,则用于保存以下scrollView和collectionView的引用:)

What hitTest does is already explained above. 上面已经解释了hitTest的作用。 Though, all it does is it checks the touch point if touch point is inside scrollView it returns the collectionView. 虽然,它所做的只是检查接触点是否在scrollView内,它返回collectionView。 What taht does is it transfers the touch event to collectionView. 这样做是将触摸事件传输到collectionView。

Now go to storyboard select the topScrollView and set its class as TestScrollView. 现在转到情节提要中,选择topScrollView并将其类设置为TestScrollView。

In ViewController which loads these scrollViews add, 在加载这些scrollViews的ViewController中,

#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. 是的,如果您已注意到它的正确性,则可以向UICollectionView添加TapGesture识别器。 Though we managed to handover the touch to UICollectionView we noticed that didSelectItemAtIndexPath was never triggered. 尽管我们设法将触摸切换到UICollectionView,但是我们注意到didSelectItemAtIndexPath从未触发。 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. 尽管在这种情况下,在此处不建议将GestureRecognizer添加到collectionView中,但还是可以的,因为我们仅覆盖了一次轻敲,所有其他手势都保持不变。

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 :) 因此,现在每当用户点击UICollectionView的gestureRecognizer上方的scrollView触发器并调用我们的选择器时:)现在,您要做的就是找出被点击的单元格并以编程方式选择它:

-(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 最后,这是项目的链接: https : //github.com/sandeeplearner/UnderlayingClickableViews

尝试将此“取景”按钮作为汽车的背景,然后将其作为汽车的imageview,以便可以单击。。我认为我不需要使视图可点击

You should add tapgesturerecognizer on upper scrollview, then you can handle tap something like, 您应该在上方的滚动tapgesturerecognizer上添加tapgesturerecognizer ,然后可以处理类似tap的操作,

- (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. 您可以考虑your car也可以代替imageview。

Hope this will help :) 希望这会有所帮助:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM