Hide UITableView search bar by default when in a navigation controller - iphone

I've read multiple posts on this but it's not working properly for me. I'm using the latest 4.2 SDK.
The code I have is
self.tableView.contentOffset = CGPointMake(0.0, 44.0);
This partially works, it moves the search bar up a little bit, but it does not get hidden completely. I've tried increasing the value 44 to something greater and this had no affect what so ever! I'm calling this code in the viewDidLoad method of the table's view controller. Does anyone have any ideas?

Another approach should be...in viewDidLoad call:
self.tableView.contentInset = UIEdgeInsetsMake(-self.searchDisplayController.searchBar.frame.size.height, 0, 0, 0);
and implementing endDragging delegate method:
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
CGPoint offset = self.tableView.contentOffset;
CGFloat barHeight = self.searchDisplayController.searchBar.frame.size.height;
if (offset.y <= barHeight/2.0f) {
self.tableView.contentInset = UIEdgeInsetsZero;
} else {
self.tableView.contentInset = UIEdgeInsetsMake(-barHeight, 0, 0, 0);
}
self.tableView.contentOffset = offset;
}
setting content is to remove some "flickering"
also if You want searchbar to stick at the top, implement didScroll this way:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGRect sbFrame = self.searchDisplayController.searchBar.frame;
sbFrame.origin.y = self.tableView.contentOffset.y;
if (sbFrame.origin.y > 0) {
sbFrame.origin.y = 0;
}
self.searchDisplayController.searchBar.frame = sbFrame;
}
I hope this will help (took me few days to figure out:) )
Cheers!
UPDATE:
As #carbonr noted You should add this line in viewDidLoad since iOS7+
self.edgesForExtendedLayout = UIRectEdgeNone;

self.tableView.contentOffset = CGPointMake(0.0, 44.0);
The above code does in fact work but it needs to run after the UITableView has finished creating all of its cells. I guess thats another question though.

You can set the initial bounds of the table view inside viewDidLoad, so the search bar appears hidden at the beginning.
You have to create the searchBar property and then use following code:
- (void)viewDidLoad
{
//...
CGRect bounds = self.tableView.bounds;
bounds.origin.y = self.tableView.bounds.origin.y + searchBar.bounds.size.height;
self.tableView.bounds = bounds;
//...
}

For others still looking for an updated solution, you can check out my answer over here.
Basically you need to update the contentOffset the first time viewDidLayoutSubviews is called.

I also have the same problem like yours. The following code solved my problem.Please add the code in you viewDidLoad() :
self.edgesForExtendedLayout = UIRectEdgeNone;
N:B: I used autoLayout in my project.

Related

Locking UISearchBar on top of TableView

I wanted to lock search bar on top of table view when y boundary reaches 0 ( or beyond - value)
I tried to in scroll view delegate method, but nothing really changed. In fact, the search bar's frame changed, but it still behaved as default.
Any ideas?
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = self.searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MAX(0, scrollView.contentOffset.y);
self.searchDisplayController.searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
}
In your viewdidload method you need to offset the table view:
self.tableView.contentOffset = CGPointMake(0.0, 44.0);
Then In IB drag a searchbar above your UITableview in your UINavigation controller
Or prgrammically add it.
You need to call setNeedsLayout if you're running on iOS 6.0+.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UISearchBar *searchBar = self.searchDisplayController.searchBar;
CGRect rect = searchBar.frame;
rect.origin.y = MAX(0, scrollView.contentOffset.y);
[scrollView setNeedsLayout]; // <-- Call setNeedsLayout here.
self.searchDisplayController.searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
}

how to fix the margin adjustment code attached for a 'Grouped" UITableView?

Trying to achieve the look & feel of a Grouped TableView, with only one item per section, and but with hardly any margins (gives me rounded edged view with the ability for the user to be able to choose the color they want).
I have it working, HOWEVER when the user changes orientation I had to use the didRotateFromInterfaceOrientation method (as willRotateToInterfaceOrientation didn't work), BUT the effect is that you do see the margins change quickly in that fraction of a second after the tableView displays.
QUESTION - Any way to fix things so one doesn't see this transition?
- (void) removeMargins {
CGFloat marginAdjustment = 7.0;
CGRect f = CGRectMake(-marginAdjustment, 0, self.tableView.frame.size.width + (2 * marginAdjustment), self.tableView.frame.size.height);
self.tableView.frame = f;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self removeMargins];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self removeMargins];
}
I Think the problem is that in willRotateToInterfaceOrientation the frame of the tableView hasn't resized yet, so your frame calculations are incorrect. on didRotateFromInterfaceOrientation the frame has already changed.
I think the easiest way to solve this is to subclass UITableView and override layoutSubviews. This method is called every time the frame of the view changes in a way that may require it's subviews to change.
The following code worked for me without an animation glitch:
#interface MyTableView : UITableView {
}
#end
#implementation MyTableView
-(void) layoutSubviews
{
[super layoutSubviews];
CGFloat marginAdjustment = 7.0;
if (self.frame.origin.x != -marginAdjustment) // Setting the frame property without this check will call layoutSubviews again and cause a loop
{
CGRect f = CGRectMake(-marginAdjustment, 0, self.frame.size.width + (2 * marginAdjustment), self.frame.size.height);
self.frame = f;
}
}
#end
You say that willRotateToInterfaceOrientation didn't work, but you didn't say why.
If the reason is that willRotateToInterfaceOrientation isn't being called, then please take a look at this question.
If you get this working then I believe your other question will solve itself.

Customize UISearchBar: Trying to get rid of the 1px black line underneath the search bar

My question is already specified in the title: I would like to get rid of the black line drawn on the bottom of the UISearchBar. Any ideas?
Here's an image of what I mean:
UPDATE:
I think that the line is part of the UITableView's tableHeaderView. I still don't know how to remove it.
Try this
searchBar.layer.borderWidth = 1;
searchBar.layer.borderColor = [[UIColor lightGrayColor] CGColor];
Why:
So, I've dug into the API's trying to figure out why this is happening. Apparently whomever wrote the UISearchBar API is rasterizing the lines onto an image and setting it as it's backgroundImage.
Solution:
I propose a simpler solution, if you want to set the backgroundColor and get rid of the hairlines:
searchBar.backgroundColor = <#... some color #>
searchBar.backgroundImage = [UIImage new];
Or if you just need a background image without the hairlines:
searchBar.backgroundImage = <#... some image #>
I have 0.5px black horizontal lines both on top and on the bottom of my UISearchBar. The only way I had so far to get rid of them is by setting its style to Minimal:
mySearchBar.searchBarStyle = UISearchBarStyleMinimal;
Solution for
XCode 10.1 Swift 4.2
I fixed this by adding a subview to the searchBar's view stack like so:
CGRect rect = self.searchBar.frame;
UIView *lineView = [[UIView alloc]initWithFrame:CGRectMake(0, rect.size.height-2,rect.size.width, 2)];
lineView.backgroundColor = [UIColor whiteColor];
[self.searchBar addSubview:lineView];
Here, self.searchBar is an UISearchBar pointer of my controller class.
Swift 4.2:
controller.searchBar.layer.borderWidth = 1
controller.searchBar.layer.borderColor = UIColor(red: 255/255, green: 253/255, blue: 247/255, alpha: 1.0).cgColor
Answer based on Ayush's answer.
This is ridiculous to have to go through hoops and bounds for this little 1 px line. I've been googling around for a couple of hours to get rid of it. I tried combos of different answer to get it to work. When I came back here I realized Oxcug already had it but it's in Objective-C and that's not native for me.
Anyway here is the answer in Swift 5. If you want to have a color background inside the actual search textField I added that too.
// these 2 lines get rid of the 1 px line
searchBar.backgroundColor = .white
searchBar.backgroundImage = UIImage()
// this line will let you color the searchBar textField where the user actually types
searchBar.searchTextField.backgroundColor = UIColor.lightGray
Set the tableHeaderView to nil before putting your UISearchBar there.
If that does not help, try to cover it up. First add your search bar to a generic and appropriately sized UIView (say, "wrapper") as a subview, then
CGRect frame = wrapper.frame;
CGRect lineFrame = CGRectMake(0,frame.size.height-1,frame.size.width, 1);
UIView *line = [[UIView alloc] initWithFrame:lineFrame];
line.backgroundColor = [UIColor whiteColor]; // or whatever your background is
[wrapper addSubView:line];
[line release];
And then add it to the tableHeaderView.
self.tableView.tableHeaderView = wrapper;
[wrapper release];
This question has already been solved but maybe my solution can help someone else. I had a similar problem, except I was trying to remove the 1px top border.
If you subclass UISearchBar you can override the frame like this.
- (void)setFrame:(CGRect)frame {
frame.origin.y = -1.0f;
[super setFrame:frame];
self.clipsToBounds = YES;
self.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0, 1.0f);
}
Or if you would like to fix the bottom pixel you could do something like this, (untested).
- (void)setFrame:(CGRect)frame {
frame.origin.y = 1.0f;
[super setFrame:frame];
self.clipsToBounds = YES;
self.searchFieldBackgroundPositionAdjustment = UIOffsetMake(0, -1.0f);
}
Only for simplicity of the example are the clipsToBounds and searchFieldBackgroundPositionAdjustment in the setFrame.
Also the searchFieldBackgroundPositionAdjustment is only needed to re-center the search field.
UPDATE
It turns out that the tableView will shift 1px from updating the origin.y while the searchBar is active. It feels a little strange. I realized that the solution is as simple as setting, self.clipsToBounds = YES;
I used
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.backgroundImage = UIImage() // Removes the line
And it worked like a charm :)
Warning. This is a hack. Would like to know a better, more official way.
You can use the pony debugger to figure out where in the subview hierarchy it is. I think the thing you are see is a private UIImageView called "separator"
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
for (UIView* view in self.searchBar.subviews) {
if (view.frame.size.height == 1 && [view isKindOfClass:[UIImageView class]]) {
view.alpha = 0;
break;
}
}
}
You can use
[[UISearchBar appearance] setSearchFieldBackgroundImage:[UIImage imageNamed:#"someImage.png"]forState:UIControlStateNormal];
on iOS 5+ to get rid of the line.
what worked with me is, setting the searchbar barTintColor as the navbar color
searchBar.barTintColor = UIColor.colorWithHexString(hexStr: "479F46")
after testing, it solves the problem in both iOS 10.x and 11.x
GIF :

UITextView text not displayed when parent view is UIScrollView UNTIL I scoll

I have a UITextView inside of a UIScrollView that is configured offscreen when the code below runs in my ViewController's viewDidLoad. Unfortunately, if the "eventDetails" text is particularly long nothing is displayed inside the UITextView until I interact with it (e.g click inside and drag for example).
My question: How to have it so the text is displayed in the UITextView WITHOUT forcing the user to interact with the UITextView first?
Here is the code:
UITextView *txtDetails = [[UITextView alloc] initWithFrame:CGRectMake(-4, yOffset, page3ScrollView.frame.size.width, 0)];
[txtDetails setEditable:NO];
[txtDetails setScrollEnabled:NO];
[txtDetails setFont:[UIFont systemFontOfSize:12]];
[txtDetails resizeAndSetTextWithMaxSize:CGSizeMake(txtDetails.frame.size.width, 999999999) forText:eventDetails withAdditionalHeightOf:16.f];
[page3ScrollView addSubview:txtDetails];
CGRect frame = txtDetails.frame;
frame.size.height = [txtDetails contentSize].height;
txtDetails.frame = frame;
[page3ScrollView setContentSize:CGSizeMake(page3ScrollView.frame.size.width, txtDetails.frame.origin.y + txtDetails.frame.size.height)];
Thanks
I was able to get this working by setting the UITextView's text ONLY when needed (e.g. is about to come on screen). Below is the relevant code:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sv
{
if (sv == scrollView) [self updatePagedView];
}
- (void)updatePagedView
{
int currentPage = pageControl.currentPage;
// *** loadDetailsPage3 is where I set the text ***
if (currentPage == 2) {
[self loadDetailsPage3];
}
}
If anyone has a better solution or needs more explanation just hit me up in the comments.
I've had the same issue and it's taken me several hours to find a suitable fix for it... Here's what I came up with...
-(void)DidBecomeActive {
if (!DidUpdateBounds) {
DidUpdateBounds = true; // instance variable set to false on load
NSString *oldtext = uiTextView.text;
//The trick is (1) removing it from it's superview
// (2) resetting it's 'text' property
// (3) re-adding it to the view WHILE it's on-screen.
[uiTextView removeFromSuperview];
uiTextView.text = oldtext;
[self.view addSubview:uiTextView];
[self.view setNeedsDisplay];
}
}
I'm using CoreAnimation to switch to this view. So right before I execute that code, I call this
//The 0,-300,480,300 is bounds of the UIView my offscreen UITextview is on
[self.view.layer setBounds:CGRectMake(0, -300, 480, 300)];
// SetNeedsDisplay, then call my method above to
//FORCIBILY redrew the UITextView while it's effectively on-screen
[self.view setNeedsDisplay];
[TextLogVC DidBecomeActive];
//CoreAnimation then goes here
This solves the issue. After setting the layer.bounds it redraws the control(UITextView) and it thinks it's onscreen. Then CoreAnimation takes care of animating from my current UIView to the new UIView and all of the text in the uiTextView is displayed throughout the animation.
Looks like the views that you are setting up are not refreshed.
And when you interact with the views then they are refreshed and your changes are rendered to the display...
Try adding [txtDetails setNeedsDisplay]; [page3ScrollView setNeedsDisplay]; after your code in viewDidLoad.
If it will help then maybe we'll find some more elegant solution...

Dynamically changing position of a UIView

Here's my setup. I have a viewcontroller that I'm creating and adding as a subview. The viewcontroller presents some options that a user can chose from. The viewcontroller is being pushed in response to a "long press" gesture. Within the viewcontroller, I added a child UIView to group some other controls together so I can move them around the screen as a unit and, when they are displayed, center them on the location of the long press. Here is the code that instantiates the view controller, changes its location, and adds it as a subview:
UserOptions *opts = [[UserOptions alloc] initWithNibName:#"UserOptions" bundle:nil];
[opts recenterOptions:location];
[self.view addSubview:opts.view];
That bit of code does create and push the viewcontroller, but the call to recenterOptions doesn't do anything. Here is that method:
- (void) recenterOptions:(CGPoint)location {
CGRect oldFrame = self.optionsView.frame;
CGFloat newX = location.x; // + oldFrame.size.width / 2.0;
CGFloat newY = location.y; // + oldFrame.size.height / 2.0;
CGRect newFrame = CGRectMake(newX, newY, oldFrame.size.width, oldFrame.size.height);
self.optionsView.frame = newFrame;
}
Note that self.optionsView is the child UIView that I added to the viewcontroller's nib.
Does anyone know why I'm unable to change the location of the UIView?
Regards,
Eric
A couple things. First, try adding the view to the view hierarchy before calling -recenterOptions:
UserOptions *opts = [[UserOptions alloc] initWithNibName:#"UserOptions"
bundle:nil];
[self.view addSubview:opts.view];
[opts recenterOptions:location];
Next, just set the center of the view instead of trying to change its frame:
- (void) recenterOptions:(CGPoint)location {
[[self optionsView] setCenter:location];
}
Are you verifying that your optionsView is valid (non-nil)?
I agree with Rob, btw. You need to change your title to match your question.
Views are loaded lazily. Until you call self.view, the view is not loaded and optionsView is nil, hence self.optionsView.frame = newFrame does nothing.