How do you exclude specific views from having a touch delay in a UIScrollView when delaysContentTouches property is set to YES?
Here's what works for me. Subclass UIScrollView, and implement only this method:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
NSLog(#"touchesShouldCancelInContentView");
if ([view isKindOfClass:[UIButton class]])
return NO;
else
return YES;
}
Related
I have a viewController that holds WKWebView in his properties.
I made my viewController to implement UIScrollViewDelegate and in 'didScroll' put some code that wont let the scrollView scroll vertically.
like that:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 0);
}
The disabling part is good I tried it on other scrollView and it works just fine.
I set the webView.scrollView delegate to be the viewController.
I also made a method that loop over all of the subViews of webView and if it comes across another scrollView it set the delegate of that scrollView to be the viewController.
After all that it still not working...
-(void)setDelegateForScrollViewsInView:(UIView *)view{
if (view.subviews.count > 0) {
for (UIView *subView in view.subviews){
if ([subView isKindOfClass:[UIScrollView class]]) {
[((UIScrollView *)subView) setDelegate:self];
[((UIScrollView *)subView) setShowsHorizontalScrollIndicator:false];
[((UIScrollView *)subView) setBackgroundColor:UIColor.blueColor];
}
[self setDelegateForScrollViewsInView:subView];
}
}I }
After View Debugger i found that my webView is build up in that hirarchy:
And my method cant catches the inside scrollView so the disabling of the vertical scroll won't work.
How can I fix it?
First take a look at the attacthed image:
My hiearchy looks as following:
UIView
UIButton
UICollectionView
UICollectionViewCell
UICollectionViewCell
UICollectionViewCell
UICollectionViewCell
UICollectionView is added to UIVew as H:[collectionView(==270)]| and V:|-(70)-[collectionView]-(70)-|.
I have two requirements:
1) only the blue part of the UICollectionViewCell should trigger the collectionView::didSelectItemAtIndexPath: method. Which I have sucesfully implemented with
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self.customView) {
return self;
}
return nil;
}
on the custom class UIColectionViewCell. The customView property is the blue/red UIView of the cell.
This works as expected.
2) I want any gestures (Tap/LongTap/Pan/...) that are done in the green part of the UICollectionViewCell or in the UICollectionVIew itself (the background that is here white with 0.5 opacity) to be passed down to the superview. For Example the turquoise UIButtion below.
The green part should also not scroll the CollectionView .. it just has to be completly transparent to any gestures.
How can I do that? Tried a lot of different approaches but none of them worked. I know how to do it with a regular UIView, but cannot get it to work on a UICollectionView and its cells.
Keep in mind that there will be other UIViews or UIButtions that will be interactable and placed under the green part of collection view. The green color will later just be UIClearColor.
Suggestion to just have the UICollectionView smaller width (width of the blue part) is not an option since the Blue/Red part of UICell has to strecth out to full width of the cell in some cases.
Here is the final solution for above example:
1) Require only red/blue part to be tappable.
This stays basically the same. I have a reference to the blue/red UIView names customView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self.customView) {
return self;
}
return nil;
}
2) Any interactions inside the UICollectionView that are not done on blue/red UIViews should be ignored.
Subclassing UICollectionView and adding overwriting this metod:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSIndexPath *indexPath = [self indexPathForItemAtPoint:point];
LCollectionViewCell *cell = (LCollectionViewCell*)[self cellForItemAtIndexPath:indexPath];
if (cell && [self convertPoint:point toView:cell.customView].x >= 0) {
return YES;
}
return NO;
}
This will check if the CGPoint is done on the customView. With this we are also supporting variable widths:
Your answer helped me a ton, thanks!
Here's the solution in Swift 3.0:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if hitView == self.customView {
return self
}
return nil
}
I am really stuck in this issue for quite a long time
I am trying to add a UIControl (which is a UIView in the end) to a UITableViewCell that i have subclasses in my own class (i made a custom cell)
on swipe, i create my UIControl class and add it to myself (the cell), so far so good. Here is the code
[self addSubview:_statusView];
However, i am adding a target action to my UIControl in the custom cell, so that the cell can handle when the UIControl says that he has recognized a touchDownEvent.
[self.statusView addTarget:self action:#selector(resetAll:) forControlEvents:UIControlEventTouchDown];
And here is what i want to do in the action, I want to remove that UIControl from self.subviews (the cell's subviews), so i set the action method to be like this
- (void)resetAll:(id)sender
{
for (UIView *view in self.subviews) {
if ([view isKindOfClass:[StatusView class]]) {
[view removeFromSuperview];
}
}
}
Can someone point out whats wrong in this code? because i can't really figure out why the view that gets added to the cell does't get removed. It seem to me the the subviews property doesn't ever contain my UIControl that i add.
- (void)resetAll:(id)sender
{
for (UIView *view in self.view.subviews) {
if ([view isKindOfClass:[StatusView class]]) {
[view removeFromSuperview];
}
}
}
or
- (void)resetAll:(id)sender
{
[sender removeFromSuperview];
}
UITableViewCell internally does some manipulations with its view hierarchy. You should add subviews not to the cell itself, but to its contentView, as stated in the docs:
If you want to go beyond the predefined styles, you can add subviews
to the contentView property of the cell.
So you have to replace
[self addSubview:_statusView];
with
[self.contentView addSubview:_statusView];
And then iterate on subviews of the contentView:
for (UIView *view in self.contentView.subviews) {
if ([view isKindOfClass:[StatusView class]]) {
[view removeFromSuperview];
}
}
I'm trying to figure out how much of the scroll view inside my UIPageViewController has been scrolled. I have the following code in the viewLoad that access the UIScrollView from UIPageViewController.
for (UIView *view in self.pageViewController.view.subviews)
{
if ([view isKindOfClass:[UIScrollView class]])
{
thisControl = (UIScrollView *)view;
}
}
So once I have the UIScrollView, called thisControl I would like to print how much has been scrolled by doing something like this:
NSLog (#"Scroll: %f",thisControl.contentOffset.x);
The problem, is that I don't really know where to put this line, or how print continuously how much has been scrolled.
Any idea?
Set the delegate to that controller(myScroll.delegate = self;) and then add this method:
- (void) scrollViewDidScroll: (UIScrollView *) scrollView
{
CGFloat y = scrollView.contentOffset.y;
NSLog(#"%f",y);
// some stuff
}
So I have a uicollectionview, which resides inside a view controller, in this VC I have some buttons and other controls, and I load data into the CollectionView, I cannot detect the click on the collectionview, but it responds to scrolls fine.
All I can think of is that sometimes I enable userinteraction on all subviews in my view ( including the collectionview) , and this works, in the sense that no controls work, and then based on another condition I enable activity.
this is an example of the code
if (_isMenuVisible) {
for (UIView *subview in [self.actualView subviews]) {
if([subview isMemberOfClass:[UIButton class]] || [subview isMemberOfClass:[UILabel class]] || [subview isMemberOfClass:[UIView class]]) {
subview.userInteractionEnabled = _isMenuVisible;
}
else {
subview.userInteractionEnabled = !_isMenuVisible;
}
}
}
else {
for (UIView *subview in [self.actualView subviews]) {
subview.userInteractionEnabled = !_isMenuVisible;
}
}
Could this affect the behavior?
Make sure your ViewController adopts the UICollectionViewDelegate protocol. Then, as mentioned above, simply override the collectionView:didSelectItemAtIndexPath: to decide what you want to do when a certain cell is clicked.
Use the indexPath to determine the row or section of the cell by accessing its row or section property.