I have been trying to forward touch events from one view to a UITableView. But I have come across a problem.
In the figure is a prototype view to illustrate the problem. The white view in the bottom half is a normal UITableView. The yellow view is a UIView that forwards touches to the middle of the UITableView using hitTest:withEvent:. The red view is a UIButton.
The goal is to forward the up and down panning touches from the yellow view to the UITableView at the bottom. However, tapping the red UIButton should still work.
I have tried 2 main approaches both of which have problems.
The first approach is to add UIPanGestureRecognizer to the yellow view that can be used to set the contentOffset of the UITableView. The problem is that inertial scrolling of the table view does not work with this approach.
The second approach is to forward touches using hitTest:withEvent: in the yellow view.
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if([self.button_red pointInside: [self convertPoint:point toView:self.button_red] withEvent:event]) {
return self.button_red;
} else {
return [self.view_table hitTest:CGPointMake(self.view_table.width/2.0,self.view_table.height/2.0) withEvent:event];
}
}
hitTest:withEvent: is only called when the touch starts in the yellow view. In other words, whenever a drag starts in the red view it won't forward the drag to the table view. Which is not good enough.
Is it possible to forward a pan gesture from the yellow view to the UITableView in a different way that keeps the red button working AND allows drags to work that start in the red button? Should I override the touch event methods in the yellow view to get this to work somehow? Is there way to do all this while keeping the inertial scrolling?
You need to add yellowView as a subview inside the UITableView.
You will also need to take care of the contentInset in order to
accommodate for yellowView height.
By default, cells will appear over this added yellowView, you need
to fix this by adjusting zPosition on the yellowView.
CGFloat height = CGRectGetHeight(yellowView.frame);
// fix yellowView's y position
yellowView.frame = CGRectMake(0, -height, CGRectGetWidth(yellowView.frame), height);
[tableView addSubview:yellowView];
yellowView.layer.zPosition = 10; // make it appear over cells
[tableView setContentInset:UIEdgeInsetsMake(height, 0, 0, 0)];
Hope this helps.
Related
Is it possible to click a table with a content inset to tap the view below it?
Here is the setup:
2 tables view controllers on top of each other.
Top Table
Bottom Table
Top table background is transparent with a top content inset revealing the bottom table.
[self.tableView setContentInset:UIEdgeInsetsMake(150,0,0,0)];
When the top table is scrolled all the way down, it does successfully reveal the bottom table view.
However, the bottom table can not receive touch events.
Is it possible to somehow make it clickable? (the cells?)
Thank you in advanced!
This was solved fairly quickly by subclassing the top container and responding with NO if the click was between the empty space.
(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
Also need to ensure that the table was also not scrolled up by checking it's contentOffset
if (point.y < topPadding)
{
if ((tableView.contentOffset.y * -1) >= topPadding)
{
// Point was inside the empty space of the tableview
return NO;
}
}
I have a scrollview where I have added different views (like tutorial).
What I wanted to have is slider with below design where on scroll I will see previous tut on left side and next on right side.
For this what I have added is scrollview with paging enabled and adding UILabel (for now) in for loop. After adding label in scrollview below is what I had.
To see the data on the left & right, what I did is uncheck clip subviews from storyboard.
However what I noticed is I can scroll only in scrollview area and not outside.
Any idea how can I make UILabel make scrolling outside & inside scrollview.
As of now to make it working, what I have done is added swipe gesture on view and making scrolling programmatically. However what I was looking is if I scroll outside scrollview, it should scroll scrollview little too.
Phewww...
Finally I managed to make it done..
What I did is added one more scrollview (dummyScrollView) with full screen width above main scrollview (mainScrollView) (which I am using to show label).
Now I enabled paging for the dummyScrollView too and implement below where I am scrolling my mainScrollView based on the factor calculation for the dummyScrollView
#pragma mark - UIScrollView Delegate
- (void) scrollViewDidScroll:(UIScrollView *)sender
{
float myFactor = 0;
// 44232 is tag for new scrollview
if (sender.tag==44232) {
myFactor = mainScrollView.frame.size.width/duplicateSV.frame.size.width;
myFactor = duplicateSV.contentOffset.x*myFac;
CGRect mCC = CGRectMake(myFactor, 0, mainScrollView.frame.size.width, mainScrollView.frame.size.height);
[mainScrollView scrollRectToVisible:mCC animated:NO]; // NO is very important... YES will not work
}
// 44231 is main scrollview tag where I won't be doing anything...
if (sender.tag==44231) {
}
}
I have created a UICollection with a custom layout to allow for scrolling both vertical and horizontal. It is a grid of equal sections and items in each section (ie 10 x 10, 20 x 20, etc). I would like to be able to put two headers that remain in view, one along the top and one along the left side. I have not found a way to do this within the UICollection itself. So, I set up UICollection along the left and another along the top. However, as the user scrolls the grid left and right and/or up and down, I want these two collections to mirror those movements.
So, my question is: Is there a way to mirror the horizontal movement of the main UICollection to the top UICollection and then mirror the vertical movement of the main UICollection to the side UICollection?
Thanks!
UICollectionView is a subclass of UIScrollView. It sends its delegate all of the messages defined in the UIScrollViewDelegate protocol.
The message you want to respond to is scrollViewDidScroll:. When your main collection view sends this message, you want to respond to it by getting its contentOffset and applying the offset to your margin collection views as appropriate.
// Implement this in your main collection view's delegate.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self synchronizeCollectionViewContentOffsets];
}
- (void)synchronizeCollectionViewContentOffsets {
CGPoint offset = self.mainCollectionView.contentOffset;
self.leftMarginView.contentOffset = CGPointMake(0, offset.y);
self.topMarginView.contentOffset = CGPointMake(offset.x, 0);
}
So, I figured it out. Using self.mainCollectionView.contentOffset always returned (0,0), so tried to use the object name I actually assigned to it through the Storyboard and the view controller. This worked.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self synchronizeCollectionViewContentOffsets];
}
-(void)synchronizeCollectionViewContentOffsets {
CGPoint offset = myCollectionView.contentOffset;
myLeftMargin.contentOffset = CGPointMake(0, offset.y);
myTopMargin.contentOffset = CGPointMake(offset.x, 0);
}
Where myCollectionView, myLeftMargin and myTopMargin are linked to the UICollectionViews through the Storyboard.
I currently have a view controller that is comprised of a Navigation bar, followed by a UIView that has two UIButtons added as subViews. There is then a UITableView underneath that begins at the bottom of the container UIView.
At the moment, when the user scrolls the UITableView it goes behind the UIView and UIButtons. What I actually want to happen is for the UIView and UIButtons to move up with the table view but only by the value of their height which in this case is 58 pixels. The flow would be like this...
1) Table scrolls and the UIView moves with it for the first 58 pixels.
2) The user continues to scroll the table but the UIView "pins" itself just out of view under the navigation bar.
3) When the user scrolls the table back down the UIView is then picked up and dragged back into view. I believe the new Facebook app does something similar in the timeline.
I don't want to set the UIView as the TableHeaderView of the table as I also have a pull-to-refresh which then sits above the buttons and looks terrible. I've tried playing around with the contentOffset properties of the underlying scrollview of the table but have hit a brick wall.
Any advice on where to start would be appreciated.
Thanks
EDIT: I am gotten a little further and using this code to move the frame of the UIView.
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog (#"Content Offset: %f", self.tableView.contentOffset.y);
NSLog (#"Button Frame: %f", self.btnBackground.frame.origin.y);
if (self.tableView.contentOffset.y > 0)
{
CGRect newFrame = self.btnBackground.frame;
newFrame.origin.x = 0;
newFrame.origin.y = -self.tableView.contentOffset.y;
[self.btnBackground setFrame: newFrame];
}
}
The problem now is that the scrollViewDidScroll delegate method doesn't get fired quickly enough if the table view is scrolled fast. The result is that the UIView doesn't quite make all way back to its original position when scroll quickly.
The scroll content offset is a good idea. Also if you tableview has only one section one approach is to do a custom header view representing the top level widgets. If there is more than one sections create an additional empty section which would return your custom header.
You can refer to this stack overflow post.
Customize UITableview Header Section
Well Asked Question (y)
well , for me i would first : use a main UIScrollView that contains both your topView and the tableView under it and that has the same width as your top UIView and UITableView and set its height to be height(tableView) + height(topView).
Second : since UITableView is a subClass of UISCrollView you can use scrollViewDidScroll delegate to know if the tableview is scrolled up or down.
in this cas you will have Two cases :
1) tableview is scrolled up = > you set the content offset of the main scrollView to be
[scrollView setContentOffset:CGPointMake(0, 58) animated:YES];
2) when the table view is scrolled down you can reset the content offset again
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
I have a UIScrollView that on it I have also created Gesture Recognizers. Tap, Double Tap, two finger tap, etc. On the Scroll View I create several other UIViews. Think of each of these views as Drawing objects. Circle, Squares, buttons, images, etc. Each of the Subviews I can pan, rotate, tap, etc and they all work for the most part.
If the Scaling Scroll View is not at 100% (1-1) then panning the subviews gets a bit sketchy. you can always tap them to get them to Highlight, though panning, rotation, etc is iffy. Typically if I try to pan a selected subview, it pans the scroll view. Sometimes it works, sometimes it does not. Set the zoom to 100%, or turn off the Scrolling (set scale to the same min/max), I can do what Is expected.
Any Suggestions on where to start troubleshooting this?
Not 100% why this works, but this is the code that made the issue disappear. I had to Subclass UIScrollView, override (BOOL)touchesShouldCancelInContentView:(UIView *)view and return NO if the view was anything but the UIView class for the UIScrollView.View
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
BOOL returnVal = NO;
if ([view isKindOfClass:[IoScreenEditorContentView class]]) {
returnVal = [super touchesShouldCancelInContentView:view];
}
return returnVal;
}