In iOS 5, is there a way to never hide the search bar in a UITableViewController?
I wouldn't recommend a UITableViewController for that then, a UIViewController with a UITableVIew and UISearchBar on top of it and not on the header would do the job. In a more personal opinion, I wouldn't recommend UITableViewController for anything, I find it too much strict for what it really offers. If for some reason I am using a UITableViewControllerand the customer asks me to add a new element to the screen, I am basically screwed.
I know it's an old question, but I found out a solution for this, which works with the classic UITableViewController and UTSearchDisplayController.
I created a container view for the searchBar 1st then put the searchbar inside it. The container must not clip to bounds. After this you can change the position of the searchbar relative to the container. One problem with this that this way the searchbar not handle user interactions. So we need to use our own container which get the events below its real frame.
Our container class:
#interface _SearchContainerView : UIView
#end
#implementation _SearchContainerView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (self.subviews.count > 0) {
UISearchBar *searchBar = (UISearchBar *) self.subviews[0];
CGRect f = searchBar.frame;
f = CGRectMake(0, 0, f.size.width, f.origin.y + f.size.height);
if (CGRectContainsPoint(f, point)) return YES;
}
return [super pointInside:point withEvent:event];
}
#end
If you create the searchBar programmatically you can set the this up with a following like code:
- (void)setSearchEnabled:(BOOL)searchEnabled {
if (searchBar == nil && searchEnabled) {
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, 44)];
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar
contentsController:self];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin
| UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchContainer = [[_SearchContainerView alloc] initWithFrame:searchBar.frame];
[container addSubview:searchBar];
container.clipsToBounds = NO;
self.tableView.tableHeaderView = container;
} else {
[searchBar removeFromSuperview];
self.tableView.tableHeaderView = nil;
searchBar = nil;
searchDisplayController = nil;
searchContainer = nil;
}
}
Then you can change the position based on the tableView's scroll position:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (searchBar == nil || searchDisplayController.isActive) return;
CGRect b = self.tableView.bounds;
// Position the searchbar to the top of the tableview
searchBar.frame = CGRectMake(0, b.origin.y, b.size.width, 44);
}
And the last part is to restore everything after searching:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
// Restore header alpha
searchContainer.alpha = 1.0;
// Place the searchbar back to the tableview
[searchBar removeFromSuperview];
[searchContainer addSubview:searchBar];
// Refresh position and redraw
CGPoint co = self.tableView.contentOffset;
[self.tableView setContentOffset:CGPointZero animated:NO];
[self.tableView setContentOffset:co animated:NO];
}
Related
I would like to add a progress bar between navigation bar and UISearchBar. May I know how can I implement this? Please help. Thank you.
Here is my current code in Cell.m
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect barFrame = CGRectInset(self.searchBar.frame, 10.0f, 10.0f);
self.searchBar.frame = barFrame;
}
Here is my current code in ViewController.m
Did not reflect in this code after edited. _searchBar=[[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 24)];
- (void)viewDidLoad {
[super viewDidLoad];
_valueProgress = [[LDProgressView alloc] initWithFrame:CGRectMake(0,DCNaviH, ScreenW, 10.0f)];
_valueProgress.type = LDProgressSolid;
_valueProgress.color = ThemeRedColor;
_valueProgress.progress = 0.40;
_valueProgress.flat = #YES;
_valueProgress.showText = #NO;
[self.view addSubview:_valueProgress];
}
- (UISearchBar *)searchBar{
if (!_searchBar) {
_searchBar=[[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 24)];
[_searchBar setBackgroundImage:[UIImage imageNamed:#"ic_searchBar_bgImage"]];
}];
}
return _searchBar;
}
There are a few simple options for you depending on your apps UX. I think the best solution for you based on how you explained your issue would be to include the progress bar to your view and make sure it's above the other views while positioning it below the navigation bar.
_valueProgress = [[LDProgressView alloc] init];
_valueProgress.translatesAutoresizingMaskIntoConstraints = NO;
_valueProgress.type = LDProgressSolid;
_valueProgress.color = ThemeRedColor;
_valueProgress.progress = 0.40;
_valueProgress.flat = #YES;
_valueProgress.showText = #NO;
[self.view addSubview:_valueProgress];
[_valueProgress.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].isActive = YES;
[_valueProgress.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].isActive = YES;
[_valueProgress.heightAnchor constraintEqualToConstant:10.0f].isActive = YES;
if (#available(iOS 11.0, *)) {
[_valueProgress.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].isActive = YES;
} else {
[_valueProgress.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].isActive = YES;
}
And if the search bar has been added above the progress bar, you can always call [self.view bringSubviewToFront:_valueProgress] afterwards.
I'm trying to figure out an approach to build something like the image below, which is a list of items that when a section is clicked slides out content. It's a really common UX on most websites and what not. My idea is to have each gray box (button) slide out a UIView containing some other items. I'm still new to iOS development but I'm struggling to find how you can animate a UIView to slide down and push the content below it down as well. Hoping some one can give me a good starting point or point to some info outside the realm of the apple docs.
Thanks!
So if you just have a few views, I would not recommend the UITableView approach, since it is not so easy to customize with animations and table views usually want to fill the whole screen with cells. Instead write a expandable UIView subclass that has the desired two states. Add a method to switch between extended and collapsed state. On expanding/collapsing adjust their positions so that they always have enough space.
I provide you an example of views adjusting their frames. I guess it should be easy to do the same with auto layout constraints: give the views a fixed height constraint and change this on collapsing/expanding. The same way set the constraints between the views to be 0 so that they are stacked on top of each other.
Expandable View:
#interface ExpandingView(){
UIView *_expandedView;
UIView *_seperatorView;
BOOL _expanded;
}
#end
#implementation ExpandingView
- (id)init
{
self = [super initWithFrame:CGRectMake(15, 0, 290, 50)];
if (self) {
_expanded = NO;
self.clipsToBounds = YES;
_headerView = [[UIView alloc] initWithFrame:self.bounds];
_headerView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self addSubview:_headerView];
_seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-1, self.bounds.size.width, 1)];
_seperatorView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:_seperatorView];
_expandedView = [[UIView alloc] initWithFrame:CGRectOffset(self.bounds, 0, self.bounds.size.height)];
_expandedView.backgroundColor = [UIColor blueColor];
[self addSubview:_expandedView];
}
return self;
}
- (void)layoutSubviews{
[self adjustLayout];
}
- (void)adjustLayout{
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, 50);
_seperatorView.frame = CGRectMake(0, 49, self.bounds.size.width, 1);
_expandedView.frame = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height-50);
}
- (void)toggleExpandedState{
_expanded = !_expanded;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _expanded?200:50);
[self adjustLayout];
}
#end
ViewController:
#interface ExpandingViewController (){
NSArray *_expandingViews;
}
#end
#implementation ExpandingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_expandingViews = #[
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
];
for(ExpandingView *view in _expandingViews){
[view.headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(expandingViewTapped:)]];
[self.view addSubview:view];
}
}
- (void)viewWillLayoutSubviews{
int y = 100;
for(ExpandingView *view in _expandingViews){
view.frame = CGRectOffset(view.bounds, (CGRectGetWidth(self.view.bounds)-CGRectGetWidth(view.bounds))/2, y);
y+=view.frame.size.height;
}
}
- (void)expandingViewTapped:(UITapGestureRecognizer*)tapper{
ExpandingView *view = (ExpandingView*)tapper.view.superview;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:0 animations:^{
[view toggleExpandedState];
[self.view layoutIfNeeded];
} completion:nil];
}
I have added a UISearchBar programatically to my UITableView, it was showing perfectly fine untill I decided to add an offset to my UITableView to hide the UISearchBar when the view is loaded. I would like help displaying it again.
This is what my code looks like.
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.contentOffset = CGPointMake(0.0f, 44.0f);
mySearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)];
mySearchBar.autocorrectionType = UITextAutocorrectionTypeNo;
mySearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
mySearchBar.keyboardType = UIKeyboardTypeAlphabet;
mySearchBar.delegate = self;
self.tableView.tableHeaderView = mySearchBar;
// Create the search display controller
UISearchDisplayController *searchController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];
searchController.searchResultsDataSource = self;
searchController.searchResultsDelegate = self;
I am not really sure where to go to from here.
This code will work in both iOS6 and iOS7.
Note that in iOS7 you will loose transparency of NavigationBar
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
self.edgesForExtendedLayout = UIRectEdgeNone;
}
[self.tableView setContentOffset:CGPointMake(0, mySearchBar.frame.size.height)];
If you want to save default transparency in iOS7 use this code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
[self.tableView setContentOffset:CGPointMake(0, -20)];
}
else {
[self.tableView setContentOffset:CGPointMake(0, mySearchBar.frame.size.height)];
}
}
I am about create "No connection" view in gap between navigation bar and view controller content.
I want to subclass UINavigationViewController and move content of view controllers inside a bit down.
Question is how to do this in right way?
My current solution is working but it is also quite hacky. I would like make it better.
// subclass of AGNavigationController
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
static BOOL firstTime = YES;
if (firstTime) {
contentView = nil;
for (UIView *v in self.view.subviews) {
if ([v isKindOfClass:[NSClassFromString(#"UINavigationTransitionView") class]]) {
contentView = v;
break;
}
}
firstTime = NO;
origFrame = contentView.frame;
noConnectionView.frame = CGRectMake(0, self.navigationBar.frame.origin.y+self.navigationBar.frame.size.height, 320, 20);
}
[self adjustToConnection:NO withAnimation:NO];
}
-(void)adjustToConnection:(BOOL)isConnection withAnimation:(BOOL)animation {
if (isConnection) {
[noConnectionView removeFromSuperview];
contentView.frame = origFrame;
} else {
[self.view addSubview:noConnectionView];
contentView.frame = CGRectMake(0, origFrame.origin.y+20, 320, origFrame.size.height-20);
}
}
Create your UIView and set its frame to something like (0, 0, self.view.frame.size.witdh, 40).
Use [self.view addSubview:myView]; to add the view to your UIViewController.
Move your elements down in your main view, with the vertical offset set at myView.frame.size.height.
This is all you can get with a question like this one. If you need more help, you have to be more precise about what you tried, and what doesn't work.
I got a TableVieController with a searchBar positioned at self.tableView.tableHeaderView . When the screen rotates I am just setting the frame to the new CGRect. The view looks alright but unfortunately half of the search bar isn't clickable, neither is the cancel button.
So how do I make this work? I only managed to do it by creating a new instance of the search bar and assign it, but that won't be the right way, right?
const CGRect CGRECT_TABLE_VIEW_PORTRAIT = { { 0.0f, 0.0f }, { 320.0f, 365.0f } };
const CGRect CGRECT_TABLE_VIEW_LANDSCAPE = { { 0.0f, 0.0f }, { 480.0f, 150.0f } };
- (void)viewDidLoad
{
...
// create search bar
searchBar = [[UISearchBar alloc] initWithFrame:self.tableView.bounds];
[searchBar setTintColor:RGB(168, 212, 255)] ;
searchBar.showsCancelButton = YES;
[searchBar sizeToFit]; // Get the default height for a search bar.
searchBar.delegate = self;
self.tableView.tableHeaderView = searchBar;
self.tableView.frame = CGRECT_TABLE_VIEW_PORTRAIT;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)
interfaceOrientation duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
tableView.frame = CGRECT_TABLE_VIEW_LANDSCAPE;
} else {
tableView.frame = CGRECT_TABLE_VIEW_LANDSCAPE;
}
}
I don't know why you're trying to deal with rotations, when this should happen automatically. This code worked to give a search bar that adjusts itself on rotation:
- (void)viewDidLoad {
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:self.tableView.bounds];
[searchBar setTintColor:[UIColor colorWithRed:168/255 green:212/255 blue:1 alpha:1]];
searchBar.showsCancelButton = YES;
[searchBar sizeToFit]; // Get the default height for a search bar.
searchBar.delegate = self;
self.tableView.tableHeaderView = searchBar;
}