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;
}
}
Related
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.
I have a requirement in which I need to have the following functionality -
1)I have a custom segmented control. In order to implement paging to the segmented control I have used horizontal scroll view. Each page has its own vertical scroll view.
Requirement
1)The image should hide as user scrolls up in the respective pages and should show down when user scrolls down in respective pages but keeping the custom segment always at the top of the screen when image is hidden irrespective of the individual page selection-
What I have tried so far -
1st Method
I tried putting the image as header of a table view.
Created a single section with one cell & gave the section header as the custom segment. And in the cell I placed the horizontal scroll view with the cell's height adjusted to cover all portion left out of the superview but it didn't work out as when I scroll the vertical scrolling of individual pages it was not in sync with the table view.
2nd Method
I tried setting the segment initially with a fixed distance from the top & I increased & decreased the constraint inside scrollViewDidScroll(). But it too didn't work as when the user scrolled rapidly ,the changing of constraint value didn't follow correctly.
So is there any other way to achieve the same ?
Please suggest as I can't make out what to do?
You add a tableView and your UIImage on top of it inside a scrollView. The tableView must have the same height & width than your scrollView. Then you disable the pan gesture of the scrollView :
self.scrollView.panGesture.active = false
Then you have to implement a custom scroll in scrollViewDidScroll' of yourtableView`'s delegate:
func scrollViewDidScroll(scrollView: UIScrollView) {
if self.scrollView.contentOffset.y <= 100 {
self.scrollView.contentOffset.y += scrollView.contentOffset.y
self.tableView.contentOffset.y = 0
} else {
// let the tableView scroll normally
}
}
Or, you can have a try with https://github.com/bryankeller/BLKFlexibleHeightBar ;)
It's a great component that can handle many type on animation in the header based on the position of a scrollView.
I've got a view that has 3 tableView's. One is the "Main Table View", and then I have an 'Answers Table View' and 'Percentage Table View'.
When the screens loads, the Main Table View occupies the top 95% of the screen. The bottom of the screen is a UIView containing 2 buttons. "Answers" and "Percentage".
The way it works, is if I click "Percentage" it changes the height of the Main Table View to 0, and gives that height to the Answers Table View. This animates the "Answers/Percentage" View to the top, and reveals either the Answers or Percentage TableView below it.
Here's an example:
As you can see, I click on "Percentage" which animates it up. If you click on "Percentage" again it animates it back down.
However, what I want to do is if the "Answers/Percentage" view is at the bottom of the View, and the user scrolls the Main Feed UP reaches the very end of the tableView's contents (not just the end, but the end and a little bit more), I want to animate it up like in the .gif.
Similarly, if the "Answers/Percentage" is at the top, and the user scrolls the lower "Answers Table View" down past a certain point where there is no more data above, it will completely animate.
Also, I do not ever want the "Answers/Percentage" view to be in the middle, and showing a tableview both top and bottom. All one, or the other, but not a bit of both. Which I have right now.
What I need to know is... how can I detect if the user has scrolled past the very top or very bottom of the table view +30 pixels for example, to initiate my animation?
You can use the contentOffset property of the table view.
if(tableView.contentOffset.y >= (tableView.contentSize.height - tableView.frame.size.height)) {
// Start the animation
}
I haven't tested this, but let me know if it works.
Making another answer based on sublimepremise's approach because it's easier to format and post code that way. The base idea is to check the table view's .contentOffset.y, e.g. by implementing scrollViewDidScroll in its delegate, and triggering your animations accordingly.
It may have a bit of a QnD feel to it, but if you need to also abort the user's dragging action when triggering the animation, an easy way to do so would be by "resetting" the table view's gesture recognizer. In code that could look something like this:
static const CGFloat kChrisTableViewAnimationThreshold = 30.0f;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.topTableView) {
if([self scrolledPastBottomThresholdInTableView:self.topTableView]) {
// Start the animation
// ...
// Toggling the table view's pangGestureRecognizer off and on cancels the gesture
self.topTableView.panGestureRecognizer.enabled = NO;
self.topTableView.panGestureRecognizer.enabled = YES;
}
}
}
- (BOOL)scrolledPastBottomThresholdInTableView:(UITableView *)tableView {
return (tableView.contentOffset.y - kChrisTableViewAnimationThreshold >= (tableView.contentSize.height - tableView.frame.size.height));
}
Note that this illustration obviously only covers recognizing when the top table view is scrolled past its bottom end, but adapting it for handling other scenarios should be pretty straightforward.
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];