Fix a UIView(from storyboard) to the top of a UITableView - ios

I've attached a UIView from the storyboard to the top of my UITableView and linked it to the code as IBOutlet, and I want it to be fixed to the top of my tableView. I've tried several ways:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
headView.center = CGPointMake(headView.center.x + scrollView.contentOffset.x, headView.center.y + scrollView.center.y);
[scrollView bringSubviewToFront:headView];
}
and
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect newFrame = headView.frame;
newFrame.origin.y = 0;
[headView setFrame:newFrame];
}
and some other ways I found in the site, but none worked for me, I affraid because I connected headView from the storyboard.
How can I do it? many thanks!

Sounds like you have a UITableViewController and have added the view to the table views header view, hence it scrolls with the table.
If this is the case try changing your main view controller to a UIViewController and add the view and tableview there.

Related

UIScrollView got freeze when Back button pressed?

I am having a (UIView and UICollectionView in a UIScrollView) as 1stVC. I have to scroll both at the same time for which I already unable the collection view scrolling. So first time When I launch that screen I am able to scroll the whole view, But when I push to (next ViewController) 2ndVC and then press Back Button to 1stVC my scroll view is not preforming it got Freeze.
Tried this Method:-
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^ {
CGRect contentRect = CGRectZero;
for(UIView *view in self.scrollView.subviews)
contentRect = CGRectUnion(contentRect,view.frame);
self.scrollView.contentSize = contentRect.size;
});
}
Tried this Method:-
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[_scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, _scrollView.frame.size.height)];
}
And For Getting Dynamic Height of the Collection view I have Done This
CGFloat height = _collectionCompass.contentSize.height;
Which I Have provided to ScollView ContentSize.
_scrollView.contentSize = CGSizeMake(_scrollView.frame.size.width, height);
Please help me.
What I really suggest it's to remove scroll view and do all stuff in collection view, since with scroll view you're missing some of benefits (e.g. cells reusing). Also collection view in scroll view it's a bad practice.
From Apple style guide:
Don’t place a scroll view inside of another scroll view. Doing so creates an unpredictable interface that’s difficult to control.
If your UI as list, you can add view as header of collection view or as another cell. With this approach you can remove the code from viewDidLayoutSubviews

How to set map view parallax effect like Citymapper app? [duplicate]

Using storyboard, I have placed an imageView as my tableView's headerView inside a ViewController.
This is how my storyboard is set up:
Depending on what data the user is viewing, the viewController will either show or hide the headerView. My question is, that when the headerView is visible and the user drags down on the tableView, how can I have the imageView stick to both the navigationBar and the tableView as it resizes to cover the space in between?
This is what it currently does:
But this is what I'm going for:
Any help would be greatly appreciated. I've looked at parallax libraries, but none support sectionTitles, and I'm not necessarily going for the parallax effect either. When the user scrolls up, I want it to bounce back to the regularView and not hide the headerView. Thanks!
UPDATE:
I have followed the advice posted by Dany below and have done the
following:
-(void)scrollViewDidScroll:(UIScrollView*)scrollView {
CGRect initialFrame = CGRectMake(0, 0, 320, 160);
if (scrollView.contentOffset.y < 0) {
initialFrame.size.height =! scrollView.contentOffset.y;
childHeaderView.frame = initialFrame;
} }
childHeaderView is an imageView and for some reason when I drag down,
the image moves up (like half of it behind the navBar) and doesn't return. Any advice would be
greatly appreciated!! Thanks!
I recently posted a blog post about accomplishing this using constraints which might help, turns out it was quite straight forward.
Here is the link: Creating parallax effect on UIScrollView using constraints
First of all you should remove the UIImageView from the header and add it as a simple UIImageView on top of the UITableView then since UITableViewDelegate protocol conforms to UIScrollViewDelegate protocol you can implement the scrollViewDidScroll: method to check when the tableView is scrolling down and has a bouncing effect. something like this:
-(void)someInitMethod {
initialFrame = yourHeaderView.frame;
}
-(void)scrollViewDidScroll:(UIScrollView*)scrollView {
if(scrollView.contentOffset.y < 0) {
initialFrame.size.height -= scrollView.contentOffset.y;
yourHeaderView.frame = initialFrame;
}
}
Also make sure you set the proper contentMode for your UIImageView. Also I think this implementation will create a bouncing effect but I'm not sure because I can't test it right now but I think this is a good start point for you.
This is how I achieved it, in my case I was using a map view up the top:
Create a View Controller in storyboard.
Add a Table View and set the constraints to 0 from all sides.
Add a Map View (or whatever view) below the Table View so that it will get rendered over the top. It will look like it is overlapping.
Add constraints to the top left and right.
In the view controller viewDidLoad add the following: tableView.contentInset = UIEdgeInsetsMake(200, 0, 0, 0) where 200 is the height of the View. This will push the contents of the table view downwards.
In the view controller add the following code, which resizes the view based on the scrolling:
func scrollViewDidScroll(scrollView: UIScrollView) {
var scrollOffset = scrollView.contentOffset.y
var headerFrame = mapView.frame
if (scrollOffset < 0) {
// Adjust map
headerFrame = CGRect(x: mapView.frame.origin.x,
y: mapView.frame.origin.y,
width: mapView.frame.size.width,
height: -scrollOffset)
} else {
// Adjust map
headerFrame = CGRect(x: mapView.frame.origin.x,
y: mapView.frame.origin.y,
width: mapView.frame.size.width,
height: 0)
}
mapView.frame = headerFrame
}
If I could set contentInset from the storyboard it would be even more pretty
Please have a look at this https://github.com/matteogobbi/MGSpotyViewController which implements the same effect as per your requirement.
The earlier solutions on this page gave me some trouble when I needed this to work along with section titles and index bar, so I came up with the following alternative myself. Please note; I don't use autolayout in my project and I've only tested this on iOS9+;
In your project's storyboard:
Create a UITableView within a UIViewController (or try it with a UITableViewController).
Drop a UIView at the top (but within) the UITableView, so it becomes a table header above the first cell.
Give this header view a desired height (like 200px for example) and set the background color to "Clear Color". The Clear Color is important, the view needs to be see-through.
Drop a 2nd UIView within the table header UIView and make it the same size as it's parent. This will be the actual header, so feel free to give it any color, setup an image view or other content.
Connect this 2nd UIView to your UIViewController IBOutlet, I named it "headerView" in my case.
Next, go to your UIViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Remove view from table header and place it in the background instead.
[self.headerView removeFromSuperview];
UIView *backgroundView = [UIView new];
[backgroundView addSubview:self.headerView];
self.tableView.backgroundView = backgroundView;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/* Set initialScrollOffset ivar to start offset, because in my case
the scroll offset was affected by the statusbar + navigation bar heights
and the view controller's "extend edges under top bars" option. */
initialScrollOffset = self.tableView.contentOffset.y;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
/* Modify headerView height only if the table content gets pulled
beyond initial offset. */
if (scrollView.contentOffset.y < initialScrollOffset) {
CGRect frame = self.headerView.frame;
frame.size.height = self.tableView.tableHeaderView.frame.size.height + -scrollView.contentOffset.y;
self.headerView.frame = frame;
}
}
I needed this implementation only for a stretching header with background color and labels. It should be easy to add a UIImageView to this header though.
Also, steps 1 to 5 are completely optional of course. You can programmatically create your header view or use a XIB instead. As long as you make sure the table has a Clear Colored header view set with the same height as your desired header because this serves as a spacer to keep your cells and section titles in line.
EDIT:
I found an even cleaner way to accomplish this;
Build up your table header in interface builder as described above: 1 UIView as container with a 2nd UIView embedded within.
Skip the viewDidLoad code above, there is no need to pull the UIView out of it's container and we won't need to set it as a table background.
Change the scrollViewDidScroll: method to this:
UIViewController.m:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y < initialScrollOffset) {
CGRect frame = self.headerView.frame;
frame.size.height = self.tableView.tableHeaderView.frame.size.height - scrollView.contentOffset.y;
frame.origin.y = self.tableView.tableHeaderView.frame.origin.y + scrollView.contentOffset.y;
self.headerView.frame = frame;
}
}
That's it. Only visual difference from the other solution is that the contents will now scroll up along with the rest of the cells instead of being overlapped by them.
I don't know, if this would help you or not ..
Set your scroll delegate to self.
and then implement this:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 0)
{
// then we are at the top
}
else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
// then we are at the end
// Do what you need here
}
}

UISearchBar hides view in iOS 7

When I initiate a search on UISearchBar which is a child off a UITableView, any other view except the UITableView becomes hidden. This issue only happens on iOS 7. And I dont have any specific code which hides the other views.
To come to the solution I first had to figure out the issue.
When text is input into the UISearchBar, it creates a UITableView which sits on top of the parent view.
To show the hidden parent view, the UITableView created must be offset and re sized to fit within a smaller area.
(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
// The tableView the search tableView replaces
CGRect f = self.mainTableView.frame;
CGRect s = self.searchDisplayController.searchBar.frame;
CGRect updatedFrame = CGRectMake(f.origin.x,
f.origin.y + s.size.height,
f.size.width,
f.size.height - s.size.height);
tableView.frame = updatedFrame;
}
Try to add this in the viewDidLoad for the popover UIViewController:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone;

iOS: Stretching / Resizing UITableView Header As The User Drags Down?

Using storyboard, I have placed an imageView as my tableView's headerView inside a ViewController.
This is how my storyboard is set up:
Depending on what data the user is viewing, the viewController will either show or hide the headerView. My question is, that when the headerView is visible and the user drags down on the tableView, how can I have the imageView stick to both the navigationBar and the tableView as it resizes to cover the space in between?
This is what it currently does:
But this is what I'm going for:
Any help would be greatly appreciated. I've looked at parallax libraries, but none support sectionTitles, and I'm not necessarily going for the parallax effect either. When the user scrolls up, I want it to bounce back to the regularView and not hide the headerView. Thanks!
UPDATE:
I have followed the advice posted by Dany below and have done the
following:
-(void)scrollViewDidScroll:(UIScrollView*)scrollView {
CGRect initialFrame = CGRectMake(0, 0, 320, 160);
if (scrollView.contentOffset.y < 0) {
initialFrame.size.height =! scrollView.contentOffset.y;
childHeaderView.frame = initialFrame;
} }
childHeaderView is an imageView and for some reason when I drag down,
the image moves up (like half of it behind the navBar) and doesn't return. Any advice would be
greatly appreciated!! Thanks!
I recently posted a blog post about accomplishing this using constraints which might help, turns out it was quite straight forward.
Here is the link: Creating parallax effect on UIScrollView using constraints
First of all you should remove the UIImageView from the header and add it as a simple UIImageView on top of the UITableView then since UITableViewDelegate protocol conforms to UIScrollViewDelegate protocol you can implement the scrollViewDidScroll: method to check when the tableView is scrolling down and has a bouncing effect. something like this:
-(void)someInitMethod {
initialFrame = yourHeaderView.frame;
}
-(void)scrollViewDidScroll:(UIScrollView*)scrollView {
if(scrollView.contentOffset.y < 0) {
initialFrame.size.height -= scrollView.contentOffset.y;
yourHeaderView.frame = initialFrame;
}
}
Also make sure you set the proper contentMode for your UIImageView. Also I think this implementation will create a bouncing effect but I'm not sure because I can't test it right now but I think this is a good start point for you.
This is how I achieved it, in my case I was using a map view up the top:
Create a View Controller in storyboard.
Add a Table View and set the constraints to 0 from all sides.
Add a Map View (or whatever view) below the Table View so that it will get rendered over the top. It will look like it is overlapping.
Add constraints to the top left and right.
In the view controller viewDidLoad add the following: tableView.contentInset = UIEdgeInsetsMake(200, 0, 0, 0) where 200 is the height of the View. This will push the contents of the table view downwards.
In the view controller add the following code, which resizes the view based on the scrolling:
func scrollViewDidScroll(scrollView: UIScrollView) {
var scrollOffset = scrollView.contentOffset.y
var headerFrame = mapView.frame
if (scrollOffset < 0) {
// Adjust map
headerFrame = CGRect(x: mapView.frame.origin.x,
y: mapView.frame.origin.y,
width: mapView.frame.size.width,
height: -scrollOffset)
} else {
// Adjust map
headerFrame = CGRect(x: mapView.frame.origin.x,
y: mapView.frame.origin.y,
width: mapView.frame.size.width,
height: 0)
}
mapView.frame = headerFrame
}
If I could set contentInset from the storyboard it would be even more pretty
Please have a look at this https://github.com/matteogobbi/MGSpotyViewController which implements the same effect as per your requirement.
The earlier solutions on this page gave me some trouble when I needed this to work along with section titles and index bar, so I came up with the following alternative myself. Please note; I don't use autolayout in my project and I've only tested this on iOS9+;
In your project's storyboard:
Create a UITableView within a UIViewController (or try it with a UITableViewController).
Drop a UIView at the top (but within) the UITableView, so it becomes a table header above the first cell.
Give this header view a desired height (like 200px for example) and set the background color to "Clear Color". The Clear Color is important, the view needs to be see-through.
Drop a 2nd UIView within the table header UIView and make it the same size as it's parent. This will be the actual header, so feel free to give it any color, setup an image view or other content.
Connect this 2nd UIView to your UIViewController IBOutlet, I named it "headerView" in my case.
Next, go to your UIViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Remove view from table header and place it in the background instead.
[self.headerView removeFromSuperview];
UIView *backgroundView = [UIView new];
[backgroundView addSubview:self.headerView];
self.tableView.backgroundView = backgroundView;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/* Set initialScrollOffset ivar to start offset, because in my case
the scroll offset was affected by the statusbar + navigation bar heights
and the view controller's "extend edges under top bars" option. */
initialScrollOffset = self.tableView.contentOffset.y;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
/* Modify headerView height only if the table content gets pulled
beyond initial offset. */
if (scrollView.contentOffset.y < initialScrollOffset) {
CGRect frame = self.headerView.frame;
frame.size.height = self.tableView.tableHeaderView.frame.size.height + -scrollView.contentOffset.y;
self.headerView.frame = frame;
}
}
I needed this implementation only for a stretching header with background color and labels. It should be easy to add a UIImageView to this header though.
Also, steps 1 to 5 are completely optional of course. You can programmatically create your header view or use a XIB instead. As long as you make sure the table has a Clear Colored header view set with the same height as your desired header because this serves as a spacer to keep your cells and section titles in line.
EDIT:
I found an even cleaner way to accomplish this;
Build up your table header in interface builder as described above: 1 UIView as container with a 2nd UIView embedded within.
Skip the viewDidLoad code above, there is no need to pull the UIView out of it's container and we won't need to set it as a table background.
Change the scrollViewDidScroll: method to this:
UIViewController.m:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y < initialScrollOffset) {
CGRect frame = self.headerView.frame;
frame.size.height = self.tableView.tableHeaderView.frame.size.height - scrollView.contentOffset.y;
frame.origin.y = self.tableView.tableHeaderView.frame.origin.y + scrollView.contentOffset.y;
self.headerView.frame = frame;
}
}
That's it. Only visual difference from the other solution is that the contents will now scroll up along with the rest of the cells instead of being overlapped by them.
I don't know, if this would help you or not ..
Set your scroll delegate to self.
and then implement this:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
float scrollViewHeight = scrollView.frame.size.height;
float scrollContentSizeHeight = scrollView.contentSize.height;
float scrollOffset = scrollView.contentOffset.y;
if (scrollOffset == 0)
{
// then we are at the top
}
else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
// then we are at the end
// Do what you need here
}
}

Locking a UISearchBar to the top of a UITableView

I want to get the behavior like this:
I have a UISearchBar, which is a table header view of my tableview. When scrolling the table, the search bar does indeed move, but if you scroll above the boundaries of the table, the search bar never stops touching the navigation bar.
I found a good answer here - Locking a UISearchBar to the top of a UITableView like Game Center
But it not works on iOS 6 - manipulations with table view header frame don't work
What can be the reason of this?
I found a solution, which works on iOS 6 and lower
Make a subclass of UITableView and override layoutSubviews method
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect rect = self.tableHeaderView.frame;
rect.origin.y = MIN(0, self.contentOffset.y);
self.tableHeaderView.frame = rect;
}
If you use the following:
[_tableView setTableHeaderView:_searchBar];
When you scroll the table view beyond the first row, the search bar should also disappear rather than sticking to the top of the view.
Without seeing your code, I can only assume that perhaps you have added the UISearchBar as your section header rather than the table header?
That because iOS6 sdk not update when scrolling, maybe same optimize. so need to call tableview layoutSubviews when scroll.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MIN(0, scrollView.contentOffset.y);
[scrollView layoutSubviews];
searchBar.frame = rect;
}

Resources