UISearchBar Disappears After Events - ios7

I have a UISearchBar in my view contained inside a UITableView above the top-most cell. Everything works fine until I push a new view controller onto the navigation stack. Once I pop the new VC and return to the original view controller the search bar does not show atop the table. Instead there is a white space between my navigation bar and the top row of my table view. However, if I click this white space then the search bar opens as if it were there normally, and when I click cancel again all is well.
I think that somehow the view for the UISearchBar is being set to be entirely white, but I have no idea why. I originally have the UISearchBar just added into the storyboard, as part of a UISearchDisplayController. How can I prevent the search bar from going white after my VC is unwound to from another controller than was pushed onto the navigation stack?

I have the same problem. I don't think it relates to the color of the search bar but to the layout of the search bar's subviews. After many trials and errors I came up with this solution:
#interface MySearchBar : UISearchBar
#end
#implementation MySearchBar
-(void) layoutSubviews
{
[super layoutSubviews];
if (self.subviews.count == 1)
{
UIView *subview = [self.subviews objectAtIndex:0];
subview.frame = self.bounds;
}
}
#end
Use MySearchBar instead of UISearchBar

Related

Shows a faded gray color view through iOS 7 Navigation bar of a UISplitViewController

I have written a test iPad app which contains only a split view.
I used the storyboard for set the split view. I did not do any modification to the generated split view controller by the storyboard. But did some modifications to the master view controller and the detail view controller as follow,
Master View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
............
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
}
Detail View Controller:
- (void)viewDidLoad
{
.....................
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
[self.navigationController.navigationBar setBackgroundColor:[UIColor whiteColor]];
}
When launch the app, I noticed that the edges of the faded view appear on the navigation bar. This issue is not exist when I do not set edgesForExtendedLayout for the view controllers.
But it is needed to add.
Any Idea. Please help.
This is an example which has provided by the Apple documentation. This issue is exist with it too. https://developer.apple.com/LIBRARY/IOS/samplecode/Popovers/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010436
I'm not sure, because I'm still learning iOS development but I'm searching for a way to removing the the default left, annoying, offset in a TableView when displaying cells. After googling, I found this answer https://stackoverflow.com/a/18986158/3022883 and if you unselect "Under Bottom Bars" the TabBar (bottom) stays grey. Each cell in a UITableViewController is reused every time you scroll, so since the space in the TabBar is not used to display a cell, I'm assuming that it's the background colour of the TableView.

iOS7 - Setting selectedIndex of UITabBarController breaks touch events along right-hand edge of screen?

I've hit a weird problem with UITabBarController on iOS7 and can't seem to find a workaround, so any help would be welcome!
Scenario:
Navigation-based app using landscape orientation on iPad.
App consists of a main view, and a second view which is a UITabBarController.
TabBarController has two tabs.
First view has two buttons - each button performs a segue to the tab bar controller and sets a different tab as selected. (i.e. button1 selects the first tab, and button2 selects the second tab).
Setting the tab is done in prepareForSegue by calling setSelectedIndex on the tab bar controller.
Outcome:
On iOS 7 I am finding that the view shown in the tab bar controller fails to register any touch events along the right-hand edge of the view! So in the storyboard shown above, the UISwitch on the right side of the screen cannot be tapped.
I've even attached a tap gesture recognizer to the views and used it to log the area of the screen that can be touched - it seems to register touch events up to about x=770 points across. The remaining 1/4 of the screen is 'untouchable'!
After the segue, if you manually switch to the other tab and switch back again, the touch events are 'fixed' and the full view responds to touches again.
This doesn't seem to be a problem on iOS 5 / 6.
Any help much appreciated as to:
What is causing this to happen in the first place (iOS7 bug / change?)
How else can I work around this? I've tried calling setSelectedViewController as well as using setSelectedIndex and this seems to be the same.
Thanks in advance.
I ended up raising this with Developer Tech Support, and it looks like a bug. This is the response I got back from Apple:
The container view that the tab bar controller sets up to contain your view controller is not being resized to account for the interface being in landscape orientation. It's dimensions at the time your view controller is displayed are 768 (width) x 1024 (height).
The view hierarchy looks like this when the selected tab's view is displayed:
UIWindow
/* Navigation Controller */
UILayoutContainerView
UINavigationTransitionView
UIViewControllerWrapperView
/* Tab bar controller */
UILayoutContainerView
UITransitionView
UIViewControllerWrapperView <-- Incorrectly sized.
/* MyViewController */
MyViewController.view
The incorrect size of UIViewControllerWrapperView does not cause a display problem because subviews are still displayed even if they are outside their superview's bounds. However, event routing is much more strict. Events on the right quarter of the screen are never routed to your view controller's view because the hit test fails at the wrongly-sized UIViewControllerWrapperView where the event falls outside UIViewControllerWrapperView's bounds.
As a workaround, I subclassed UITabBarController, and added the following in viewWillAppear:
#implementation FixedIOS7TabBarController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Fix the frame of the UIViewControllerWrapperView
self.selectedViewController.view.superview.frame = self.view.bounds;
}
#end
Hope that helps someone else....
As explained in this answer,
The container view that the tab bar controller sets up to contain your
view controller is not being resized to account for the interface
being in landscape orientation. Its dimensions at the time your view
controller is displayed are 768 (width) x 1024 (height).
I was encountering this problem when the TabBarController was originally displayed in portrait mode. When the device was rotated into landscape mode, the view was unresponsive on the right hand side.
The solution proposed in that answer did not work for me, because viewWillAppear: is invoked only once. However, viewDidLayoutSubvews is invoked whenever the view changes, including rotations, so my solution was to subclass UITabBarController and perform the workaround in viewDidLayoutSubvews:
#implementation FixedIOS7TabBarController
- (void)viewDidLayoutSubviews
{
// fix for iOS7 bug in UITabBarController
self.selectedViewController.view.superview.frame = self.view.bounds;
}
#end
End up finding a workaround here:
self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Right answer don't worked for me, cause user can change orientation; And it still not touchable in some area when change orientation.
So I create my own solution, I don't sure that is normal solution.
#implementation FixedIOS7TabBarController
- (UIView*)findInSubview:(UIView*)view className:(NSString*)className
{
for(UIView* v in view.subviews){
if([NSStringFromClass(v.class) isEqualToString:className])
return v;
UIView* finded = [self findInSubview:v className:className];
if(finded)
return finded;
}
return nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIView* wraperView = [self findInSubview:self.view className:#"UIViewControllerWrapperView"];
wraperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
#end
Works perfectly for me!
In the list of view controllers on the left hand side navigate to the views/view controllers affected, drag the view to underneath the first responder so that it is disassociated to the view controller's view.
Then go to the layout tab on the right hand side, select all 4 anchors and both sets of resizing arrows (horizontal + vertical).
Then drag the view back to where it was originally (just below the view controller).

Putting a custom view into a UITableView

I have a regular-style UITableView—the one that has a white background and gray horizontal lines to separate the rows.
I have another custom UIView that is just a 100x100 rectangle filled with redColor.
How can I put the latter into the former, such that it appears over the horizontal lines, but is still a “part” of the table view in the sense that when I scroll the table view around, the red view scrolls with it? In fact, I should also be able to put my finger on the red area and scroll the table view.
Once again, if the red view is placed to overlap some horizontal lines, it should appear over the lines. Sadly, when I just add the red view as a subview to the table view, the horizontal lines go over the red view; see this screenshot.
How can this be accomplished?
The correct place to handle the stacking order of your red square is in the layoutSubviews method. The table view sends itself layoutSubviews any time it adds or removes subviews (and at other times).
You need to make a subclass of UITableView that has a reference to the red square:
#interface MyTableView : UITableView
#property (weak, readonly) IBOutlet UIView *redSquare;
#end
You can initialize redSquare in whatever way you want. I just put it in my nib along with the table view, and moved it to be a subview of the table view in awakeFromNib:
#implementation MyTableView
#synthesize redSquare = _redSquare;
- (void)awakeFromNib {
[self addSubview:self.redSquare];
}
Anyway, to actually make sure the red square is always on top of the table cells and grid lines, override layoutSubviews like this:
- (void)layoutSubviews {
[super layoutSubviews];
[self bringSubviewToFront:self.redSquare];
}
EDIT
If you are trying to add the view a above the lines (hide the lines) try to use – bringSubviewToFront: to take it to the front of the table view.
[self.tableView bringSubviewToFront:redView];
ELSE
Add the view to the self.tableView.tableHeaderView this will place it above the table view and will scroll with the table view.
UIView *redView = [[UIView alloc]init];
redView.frame = //set the frame
redView.backgroundColor = [UIColor redColor];
self.tableView.tableHeaderView = redView;
Good Luck
Just add this view to UITableView as subview:
[tableView addSubview:myRedView];
See userInteractionEnabled property in order to handle interaction and scrolling.
Make your view a subview of any normal subview of the UITableView: a header, a footer or any UITableViewCell.
I think what you've described can best be achieved using a UITableViewCell or even a subview of the header. A cell has the inherent ability to scroll the table and can be customized any way you like it. It's essentially a view.
In your situation, for example, you may want the red box to appear by default at the top of the table. You would make the first cell a 'red box' cell, where you would insert your red box into the cell's content view.
So, your problem is basically, that the UITableViewCells of a UITableView are added as Subviews dynamically, and you cannot control wether they are added in front of or behind your view.
So to keep your view at the front, you need to get it back there every time cells may be added, which occurs when the UITableView scrolls.
I would suggest you try adding your custom view as a subview and then override -scrollViewDidScroll like so:
- (void)scrollViewDidScroll {
[super scrollViewDidScroll];
// myCustomView is your custom view referenced in an IVar
[self.tableView bringSubviewToFront:myCustomView];
}
Edit your viewWillAppear delegate with these lines
UIView *redView=[[UIView alloc]initWithFrame:CGRectMake(30,30, 100, 100)];
redView.backgroundColor=[UIColor redColor];
[self.tableView addSubview:redView];

iOS: UITabBarController - lazy loading of item views

I have read that subclassing a UITabBarController is a non-recommended practice. However, how then is possible to implement lazy loading of the views of tab bar items?
I'm playing around with a standard app view hierarchy: In my main app delegate file (application:didFinishLaunchingWithOptions:) , I'm instantiating a custom subclass of UITabBarController with a 4 tab bar items / icons. I only need to load the first view of the first tab bar item - other views should be loaded lazily upon request. So, in application:didFinishLaunchingWithOptions:, I'm loading welcome view and pushing it into the first UINavigationController(1). The other tab bar items are empty UINavigationControllers - (2, 3, 4). Here is my custom subclass of UITabBarController that is also a delete of himself:
#interface MainUITabBarController : UITabBarController <UITabBarControllerDelegate> {
}
#end
#implementation MainUITabBarController
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item{
UIImageView *image = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"TableViewBG.jpg"]] autorelease];
UIViewController *vc = [self.viewControllers objectAtIndex:item.tag];
vc.view = image;
}
#end
Currently, the view of the second tab bar item is just a UIViewController but I it will become a UINavigationController soon :)
So, back to the problem, what other (more apple-complaint) options would be to implement tab bar item action handling for lazy-loading?
If I understand your question correctly, by default TabbarController loads item's view in lazy mode. That is, unless you click on a certain item on tab bar, it won't be loaded.
You can check this by putting log on each view's viewdidload and see when it is getting called.

Adding a UINavigationController as a subview of UIView

I'm trying to display a UILabel on top of a UINavigationController. The problem is that when I add the UILabel as a subview of UIWindow it will not automatically rotate since it is not a subview of UIViewController (UIViewController automatically handles updating subviews during rotations).
This is the hierarchy I was using:
UIWindow
UILabel
UINavigationController
So I was thinking I could use the following hierarchy:
UIWindow
UIViewController
UIView
UILabel
UINavigationController
This way the label could be displayed on top of the UINavigationController's bar while also automatically being rotated since it is a subview of UIViewController.
The problem is that when I try adding a UINavigationController as a subview of a view:
[myViewController.view addSubview:myNavigationController.view];
it will appear 20 pixels downwards. Which I'm guessing is because it thinks it needs to make room for the status bar. But, since the UINavigationController is being placed inside a UIView which does not overlay on top of the status bar, it is incorrectly adding an additional 20 pixels. In other words, the top of the UINavigationBar is at the screen's 40 pixel mark instead of at 20 pixels.
Is there any easy way to just shift the UINavigationController and all of its elements (e.g. navigation bar, tool bar, root view controller) up 20 pixels? Or to let it know that it shouldn't compensate for a status bar?
If not, I guess I would need to use my first hierarchy mentioned above and figure out how to rotate the label so it is consistent with the navigation bar's rotation. Where can I find more information on how to do this?
Note: by "displaying a label on top of the navigation bar", I mean it should overlay on top of the navigation bar... it can't simply be wrapped in a bar button item and placed as one of the items of the navigation bar.
Using this code seems to work:
nav.view.frame = CGRectMake(nav.view.frame.origin.x, nav.view.frame.origin.y - 20,
nav.view.frame.size.width, nav.view.frame.size.height);
I did this before adding the navigation controller as a subview. Using the [UIApplication sharedApplication].statusBarFrame instead of the hard coded 20 would probably be a good idea too.
I'm not sure if it's the best way to do it though.
If you want a frame representing the available content area, then you should just use: [[UIScreen mainScreen] applicationFrame]. Of course, this restricts your top-level view controller so that it can only be top level. So still kind of dodgy, but less so.
Why don't you use App Frame instead of adding some values to origins? I mean using:
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
as a reference frame, and do something like this:
nav.view.frame = CGRectMake(appFrame.origin.x, appFrame.origin.y, ...
This one worked for me.
I had this same problem actually but managed to fix it.
I noticed that my view controller's view had the correct frame, but the view controller's navigation bar did not (it had a frame origin of (0,20) ).
Insert this into the view's controller that is the superview of the navigation controller:
- (void) viewDidAppear:(BOOL)animated {
if (navigationController.navigationBar.frame.origin.y != 0) {
[[navigationController view] removeFromSuperview];
[[self view] addSubview:navigationController.view];
}
}
Swift 5:
add the following line in the viewDidLoad() of the root view controller of the UINavigationController.
self.edgesForExtendedLayout = [.top, .bottom]