Why is this UISearchBar getting resized? - ios

As shown below, my UISearchBar is getting resized when I tap on the search field. It animates nicely to cover the navigation bar, and then pop... it shrinks.
The setup
The UISearchBar is inside a vanilla UIView set as the tableHeaderView. I'm using a UIView (as opposed to setting the UISearchBar as the header) because I would like to put additional views in the header.
The view is defined in a XIB file and the UISearchBar is anchored to all of its borders. The constraints don't seem to matter; if I remove them, the same problem happens.
Experiments
Examining the view hierarchy in Reveal tells me that the UIView has the right size and the UISearchBar has width 1 (?!) when this happens.
As an experiment, I subclassed the UISearchBar and made intrinsicContentSize return {320,44}. With this the UISearchBar shows properly, but when I press Cancel I get the following exception:
*** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2903.23/UIView.m:8540
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
The workaround
If I create everything by code, it just works.
_headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44)];
searchBar.delegate = self;
[_headerView addSubview:searchBar];
_searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
_searchDisplayController.searchResultsDelegate = self;
_searchDisplayController.searchResultsDataSource = self;
_searchDisplayController.delegate = self;
_tableView.tableViewHeader = _headerView;
What is going on here? I clearly won't use nibs to work with UISearchBar anymore, but I would like to understand what the heck happened anyway.

The difference is in the search bar's value of translatesAutoresizingMaskIntoConstraints. It defaults to NO for the XIB-based view and YES for the code-based view. Apparently, the search controller assumes a value of YES and doesn't set up any explicit constraints when it transplants the search bar.
Setting the value to YES in code should fix it (verified locally).
UPDATE
As noted in the comments, even with translatesAutoresizingMaskIntoConstraints = YES, the controller does not restore the header view to it's original state on cancel. But there does seem to be a workaround. You can create an outlet to your header view and restore it yourself in searchDisplayControllerDidEndSearch. I did a crude proof-of-concept (and updated my sample project):
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
self.tableView.tableHeaderView = self.headerView;
[self.searchBar removeFromSuperview];
self.searchBar.frame = CGRectMake(0, 20, 320, 44);
[self.headerView addSubview:self.searchBar];
}

Following on from what both #TimothyMoose and #hpique have said about setting translatesAutoresizingMaskIntoConstraints = YES (yuck)
In my code I have found that if I do the following to show the searchDisplayController
self.searchDisplayController.searchBar.translatesAutoresizingMaskIntoConstraints = YES
[self.searchDisplayController setActive:YES animated:YES]
and do the opposite when closing the searchDisplayController,
self.searchDisplayController.searchBar.translatesAutoresizingMaskIntoConstraints = NO
[self.searchDisplayController setActive:NO animated:YES]
Then my view remains unchanged (everything goes back to what I'm expecting)
if i do not call the following line when closing
self.searchDisplayController.searchBar.translatesAutoresizingMaskIntoConstraints = NO
Then the view screws up.
The reason for this I believe is that the NSAutoresizingMaskLayoutConstraint are asserting them self and as they are the last in the constraint list, the Layout engine brakes the real constraint to satisfy the unwanted NSAutoresizingMaskLayoutConstraint constraints.

Related

ios uitableview bounce went wrong

I implemented a horizontal table view and it looks like this
The category bar, Dining Shopping something, is a horizontal table view and here is the implementation code.
LogInfo(#"Show Category Table start");
// add categories to be subview controller
self.categoriesTable = [[UITableView alloc] init];
self.categoriesTable.transform = CGAffineTransformMakeRotation(-M_PI * 0.5);
[self.categoriesTable setFrame:CGRectMake(0, 64, SCREENWIDTH, 44)];
self.categoriesTable.showsVerticalScrollIndicator = NO;
self.categoriesTable.showsHorizontalScrollIndicator = NO;
self.categoriesTable.pagingEnabled = YES;
self.categoriesTable.delegate = self;
self.categoriesTable.dataSource = self;
self.categoriesTable.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.superView addSubview:self.categoriesTable];
LogInfo(#"Show Category Table Finished");
self.categoriesTable.backgroundColor= [UIColor redColor];
It works as expect but if I change view, for example, I click any button to go to other view and go back to this view. The tableview looks like this
This problem also happpens even if I disable the bounce effect of the table view. Does anyone know how to solve this problem? Thanks!
Rather than applying a transform to the table to make it horizontal, use a collectionView with a horizontal layout.
Edit: If you want to continue using your current setup, try disabling automaticallyAdjustsScrollViewInsets on your view controller.
self.automaticallyAdjustsScrollViewInsets = NO
Edit 2: If you're curious, since iOS 7 every view controller looks at its topmost/first scroll view and adjusts its contentInsets to compensate for the navigation bar transparency which is enabled by default. In this case of course, such behaviour isn't desired at all.

UISearchBar changes its superview when it is added to UITableViewCell on iOS 7.1

In my project i need an UISearchBar to scroll on an UITableView. So i just put it on an UITableViewCell like this:
searchBar = [[SearchBar alloc] initWithFrame:cell.bounds];
searchBar.delegate = self;
searchBar.placeholder = NSLocalizedString(#"Search friends", "");
searchBar.layer.borderWidth = 1.f;
searchBar.layer.borderColor = [UIColor whiteColor].CGColor;
sdc = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:nil];
sdc.delegate = self;
[cell addSubview:searchBar];
and it works fine on iOS 6. But on iOS 7 it switches to another superview (and literally disappears from the screen) when just getting in focus. At first i thought it's something wrong with my project which causes such a behaviour but then i created a simple test project and i verified this - indeed, it's a rule, on iOS 7.1 UISearchBar added to UITableViewCell moves to another superview and gets lost from the screen right after becoming first responder.
I have overridden willMoveToSuperview: method on the search bar and i see that it moves from UITableViewCellScrollView to a view which class is UIView and whose superview's class is UISearchDisplayControllerContainerView.
After few hours of search and experiments i'm not able to figure out what causes this behaviour and how to escape it. All i know for sure is that it happens right between calls to these two UISearchDisplayDelegate methods: searchDisplayControllerWillBeginSearch: and searchDisplayControllerDidBeginSearch:.
Did anyone ever encountered this? Any ideas how to resolve it?
Are you sure you need this bizarre way to add UISearchBar to UITableViewCell to just scroll it? I simply use smthng like this:
UISearchBar *mainSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
mainSearchBar.delegate = self;
UISearchDisplayController *searchCon = [[UISearchDisplayController alloc] initWithSearchBar:mainSearchBar contentsController:self ];
searchCon.delegate = self;
searchCon.searchResultsDataSource = self;
searchCon.searchResultsDelegate = self;
mainTableView.tableHeaderView = mainSearchBar;

UISearchBar automatically resizes and changes frame

I have an issue with a search bar that behaves in a strange way when it becomes a firstResponder and when it resigns.
The search bar is added as the header of a table view
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 44.0f)];
self.searchBar.translucent = NO;
self.searchBar.barTintColor = [UIColor grayColor];
self.tableView.tableHeaderView = self.searchBar;
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar
contentsController:self];
self.searchController.searchResultsDataSource = self;
The view controller is set a left panel of JASidePanelController and it hides the center panel when the keyboard shows or hides :
- (void)keyboardWillAppear:(NSNotification *)note
{
[self.sidePanelController setCenterPanelHidden:YES
animated:YES
duration:[[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
self.searchBar.showsCancelButton = YES;
}
- (void)keyboardWillDisappear:(NSNotification *)note
{
[self.sidePanelController setCenterPanelHidden:NO
animated:YES
duration:[[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
self.searchBar.showsCancelButton = NO;
}
Normal state
When the search bar becomes a firstResponder it either moves about a point up or point down randomly
And When the search bar resigns it animates up to reach the window origin and then back to its natural frame
Here is a sample project reproducing the bug.
EDIT :
As per #kwylez suggestion, the unwanted animation that the search bar makes when it resigns can be avoided by:
self.searchBar.clipsToBounds = YES;
I solved this issue by creating a UIView with ClipBounds sets to YES and then add subview the searchbar inside it.
Then include it in tableview header. its working now.
Thanks
You initialize a search display controller with a search bar and a view controller responsible for managing the data to be searched. When the user starts a search, the search display controller superimposes the search interface over the original view controller’s view and shows the search results in its table view.
customized your searchbar view
Fixed - UISearchBar-bug-master
I traced the issue to the function "_layoutSidePanels" in the JASidePanelController.
In your app delegate, I commented out the following code and it seems to fix the grey view growing and shrinking.
rootViewController.shouldResizeLeftPanel = YES;
If you follow the code through, when the searchbar is selected you call setCenterPanelHidden, which subsequently calls _layoutSidePanels, which runs the following code:
if (self.leftPanel.isViewLoaded) {
CGRect frame = self.leftPanelContainer.bounds;
if (self.shouldResizeLeftPanel) {
frame.size.width = self.leftVisibleWidth;
}
self.leftPanel.view.frame = frame;
}
Changing the frame of the sidepanel seems to be the cause, and as I said commenting that code out fixes the issue on my end.
Edit: Also at first it seemed like the search bar was moving up and down a point, but upon further inspection it appears that it is always slightly underneath the navigation bar, but you don't notice it until you select the searchbar and the rest of the view "greys" out, so that little space that was white between the blue nav bar and light grey search bar becomes dark grey like the rest of the tableview below.
Edit #2: Took me a while, but I managed to figure out where the heck that grey mask was coming from. Your UISearchDisplayController is what is responsible for the greyish background that appears when the search bar becomes first responder, and when I removed the following two lines of code the issue you were seeing went away:
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchController.searchResultsDataSource = self;
Doing this was just to demonstrate the cause of the issue, but removing those lines of code disable whatever functionality you were going to gain from using the search display controller. I don't know exactly what you're hoping to do, so I can't really give you any advice about how to proceed, but hopefully I've pointed you in the right direction as to the causes!

bad_Access exception when typing in UITextView

I don't know what is wrong with this code; when i start typing in the UITextView, the program terminates with exc_Bad_Access exception.
UIView *toolbar = [[UIView alloc] initWithFrame:CGRectMake(0, 430, 320, 44)];
toolbar.backgroundColor = [UIColor lightGrayColor];
UITextView *sendTextView = [[UITextView alloc] initWithFrame:CGRectMake(10, 9, 240, 26)];
sendTextView.backgroundColor = [UIColor whiteColor];
sendTextView.inputAccessoryView = toolbar;
sendTextView.layer.cornerRadius = 12.0;
[toolbar addSubview:sendTextView];
[self.view addSubview:toolbar];
The above code is inside the viewDidLoad method of a UIViewController which has a UIScrollView as its view.
Putting an editable text view in a toolbar seems strange. (What do you do when the user wants to edit it? Move it up above the keybaord? I wouldn't expect a toolbar to move OR to contain an editable field.) Nevertheless, I'd be surprised if doing that caused EXC_BAD_ACCESS.
Your problem is more likely in code that actually runs when you're typing, such as one of the text view delegate methods. If you can't find anything there, please post the stack trace at the time of the crash and code for the method that was actually running at the time.
Update: After you pointed it out in your comment, I see that you're setting the toolbar as the input accessory view for sendTextView and adding it to the view controller's view. I'd guess that what's happening here is that when you start editing the text view, the text view adds the toolbar to the keyboard's view without first removing it from the view controller's view. A given view can only be part of one view hierarchy at a time; adding it to your view and using it as the input accessory view won't work. If you look at Apple's sample code for using an accessory view you'll find that the view used as the accessory isn't part of the normal view hierarchy.

UIScrollView won't show in UIView

My app is building purely programmatically on UITabBarController above a UINavigationController, which are both declared in my AppDelegate. Inside my navigationcontroller, I'm showing a UIViewController, a custom class. This custom class should show a custom UIView in a UIScrollView and a UIPageControl.
Heres my problem:
self.view seems to create an EXC_BAD_ACCESS error when I call it without declaring self.view = [[UIView alloc] init] (or similar). I was wondering if this was a problem with -(void) loadView but seems like it produces the same error in -(void)viewDidLoad. So I basically had to use self.view = scrollView to even show my scrollView, considering [self.view addSubview:scrollView] produced an error. My UIPageControl should stay on the page all the time, and actually be another part of the view than the UIScrollView. So I tried to add a container-view like this
Code:
container = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,0,0)];
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
// EDIT: ofcourse, I'm also resizing the frame with [scrollView setContentSize:] later, but this is farfetched code to add here.
[container addSubview:scrollView];
self.view = container;
Unfortunately, it seems that I don't get any result at all, and what appears is just an empty view. However, if I add a UILabel or similar, it shows:
[container addSubview:[[UILabel alloc] initWithFrame:CGRectMake(50,50,50,50)]]; // entered above or beneath the addSubview:scrollView line.
My question is: Why doesn't my scrollView appear in the UIView container?
I've noticed that some tutorials say that scrollView must have a delegate, and I agree with the logic - however I can't seem to find out how I set that delegate when I am in my CustomUIViewController-class instead of my AppDelegate.
after you change the UIScrollView size you should use:
[scrollView setNeedsDisplay:YES];
also you implement Delegates the same way you do in other classes:
.h:
#interface MyClass : NSObject <UIScrollViewDelegate>
Okay, the problem seemed to be the initialization - I didn't realize that frame and content was two different things. Seems like the frame that is initializing the view should be whatever size the view should fill, while content is the actual content of whatever should be scrolled. So when I was having problems with user interaction, it was really this.
The problem of why it didn't show in the first place was (stupid.) that the frame was initially, and never changed from, 0,0 so I really lied in my first post.
Thanks to UIScrollView and PageControl: space between views who solved my problem with user interaction.
My steps was to backtrace from self.view:
NSLog(#"%f\n%f",
((UIScrollView*) [[self.view subviews] objectAtIndex:0]).frame.size.width,
((UIScrollView*) [[self.view subviews] objectAtIndex:0]).frame.size.height);
when I realized these were 0 and 0, fixing the problem wasn't too hard :) thanks though, for your efforts Kristian.

Resources