I am creating a master detail application using monotouch for iPad. In the master view I added, a custom UIViewController. This UIViewController has a tool bar at the top and 2 UITableView. I can only see the first UITableView. I cant see the tool bar and the other UItableView at the bottom.
I am not sure if I need to turn on anything or configure anything to enable the visibility.
I created outlet for each of the table views and toolbar.
I would appreciate if anyone could shed some lights on this.
Please see the image.
Thanks
Balan Sinniah
UPDATE : I have AppDelegate code as below
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
// class-level declarations
UIWindow window;
UISplitViewController splitViewController;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
window = new UIWindow (UIScreen.MainScreen.Bounds);
var controller = new RootViewController ();
var navigationController = new UITabbedViewController();
var detailViewController = new UIDetailViewTabbedBarController();
splitViewController = new UISplitViewController ();
splitViewController.WeakDelegate = detailViewController;
splitViewController.ViewControllers = new UIViewController[] {
navigationController,
detailViewController
};
window.RootViewController = splitViewController;
navigationController.DetailViewController = detailViewController;
// make the window visible
window.MakeKeyAndVisible ();
return true;
}
}
My Navigation Controller is UITabbedView Controller which has 2 UIViewController. I am adding the toolbar and 2 Table Views in one of the UIViewController.
I got it working by adjusting the autosizing section in interface builder, mark the left, right and upper red lines and unmark the bottom red line, then everything looks fine to me.
I did the same for the UITableView , I umarked the red line in top.
For the toolbar, try once to implement this on your uiviewcontroller
(it can be the other way round to (so false in the first and true in the second)
public override void ViewWillAppear (bool animated) {
base.ViewWillAppear (animated);
this.NavigationController.SetNavigationBarHidden (true, animated);
}
public override void ViewWillDisappear (bool animated) {
base.ViewWillAppear (animated);
this.NavigationController.SetNavigationBarHidden (false, animated);
}
For the table, are the 2 tableview listed underneath each other?
(in the viewbuilder make the first tableview less high, it will adapt to the amount of data automatically when you run the application )
Related
I have a problem with my personal project.
This is my architecture :
Then, my MainViewController can push to TabBarController or another single Controller.
The problem is when I push to the TabBarController, it displays me 2 navigationBar in my 1st controller (from the tabbar)
I can hide the MainController navigation bar to display only the navigation bar from the tabbar but I dont think this is the best practice.
How can I do this ?
Thanks for help.
I think the best solution is that change the Window.RootViewController in AppDelegate , so that you don't need to deal with multiple navigation bars.
Set NavigationController with MainViewController first in AppDelegate
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
window.RootViewController = new UINavigationController(new MainViewController());
window.MakeKeyAndVisible();
return true;
}
public void changeRootVC()
{
window.RootViewController = new TabController();
}
}
Change RootViewController instead of pushing to TabbarController in MainViewController
AppDelegate app = UIApplication.SharedApplication.Delegate as AppDelegate;
app.changeRootVC();
I am running 2015 Visual Studio with Xamarin. When I launch the iOS Simulator, it is showing a black screen on the simulator. I am using a Storyboard with a Navigation Controller.
I have the Main Interface set to the my storyboard.
Size Class - Any / Any
I have restarted the simulator, ran "Reset Content and Settings"
AppDelegate code (only method with contents.):
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// If you have defined a root view controller, set it here:
Window.RootViewController = new Controllers.RegistrationController();
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
You could try something like :
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
string storyboardName = "Main";
UIStoryboard storyboard = UIStoryboard.FromName(storyboardName, null);
// if it is the first viewcontroller
UIViewController vc = storyboard.InstantiateInitialViewController();
// If you have defined a root view controller, set it here:
Window.RootViewController = vc;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
if your storyboard is called Main.stroyboard try using this:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
return true;
}
I tested both of these options and both should work when your Storyboard is called Main
You need to have a segue from your UINavigationController to an UIViewController with the content in it. If you don't have a segue then your app is going to show a black screen since the app doesn't think it has any content.
If I understand you correctly, your storyboard may look like this: Storyboard 1
And you need to create a segue between the two view controllers so that your storyboard looks like this with the segue-arrow between the two view controllers: Storyboard 2
(iOS 7 Xcode 5.0.2)
I used following methods, successfully change the status bar color to white on root view controller
[self setNeedsStatusBarAppearanceUpdate]; // Update status bar style
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent; // Set status bar color to white
}
Now I'm trying to change status bar color to black when navigate to child view controller, I don't know how to do it.(status bar color is still white)
I searched, and find this method: childViewControllerForStatusBarStyle
I read Apple's document,But still don't know how to/where to use it, and I'm not sure if this is the right approach
Anyone knows how to change status bar color in child view controller?
By default, it seems that UINavigationController unfortunately doesn't provide a sensible default implementation of childViewControllerForStatusBarStyle. By implementing this method, you can tell your navigationController to defer all calls to preferredStatusBarStyle to its topmost childViewController.
You could either subclass UINavigationController and implement the method there, or simply add a category:
#implementation UINavigationController (ChildStatusBarStyle)
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
#end
I just find out:
When you embed the root view controller inside UINavigationController correctly, You'd never need to create a category to expand the capability of navigation controller, or subclassing UINavigationController for the same purpose.
You just need to put preferredStatusBarStyle inside every view controller, and remember to call [self setNeedsStatusBarAppearanceUpdate]; to update status bar style. Simple as it is!
check out this video from WWDC 2013: Click Here
EDIT:
The reason I made it working, is I happen to set UINavigationBar hidden. In this case, it behaves the same when not using UINavigationController at all.
When you Trying to change StatusBarStyle of an UIViewController which is inside UINavigationController stack. It will fail to work in this way. It only works in individual UIViewController. The WWDC 2013 Video example is not using UINavigationController, so that why the approach is working fine.
James Frost answer was the only one that worked for me. Thank you! Here's a Swift 4 version of that code.
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
return topViewController
}
}
Note: this is a bit unwieldy as-is, I recommend adding some code to limit its scope to a single viewController. Something like this:
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
if topViewController is MyViewController {
return topViewController
} else {
return nil
}
}
}
You'll obviously need to replace MyViewController with your UIViewController subclass that implements preferredStatusBarStyle.
override var preferredStatusBarStyle: UIStatusBarStyle {
if isBackgroundDark() {
return .lightContent
} else {
return .default
}
}
Again isBackgroundDark() is yours to implement.
Finally don't forget to call setNeedsStatusBarAppearanceUpdate() in your viewController every time isBackgroundDark() changes its value.
In contrast to what James Frost said, and after much time spent debugging why my Browser Activities had wrong StatusBar colors (Swift):
override func childViewControllerForStatusBarStyle() -> UIViewController? {
return visibleViewController
}
That said: In some scenarios .topViewController is right, in others like with UIActivities it's .visibleViewController.
Thanks to #James Frost, the solution works well.
I didn't make it work at first, so I want to make a further explanation about it.
If you have a subclass of UINavigationController,
it's important to add preferredStatusBarStyle in your UINavigationController subclass at the same time.
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
And add childViewControllerForStatusBarStyle in extension of UINavigationController.
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
return visibleViewController
}
}
BTW, it's OK that UINavigationController subclass and extension of UINavigationController use different coding language, still work.
I've tried another solutions and noticed that the Status Bar doesn't update without setNeedsStatusBarAppearanceUpdate(). There can be multiple places to call this marker, but the easies way is to override viewControllers setter.
class StatusBarNavigationController: UINavigationController {
override var childForStatusBarHidden: UIViewController? {
return topViewController
}
override var viewControllers: [UIViewController] {
didSet { setNeedsStatusBarAppearanceUpdate() }
}
}
Then you can use the StatusBarNavigationController programmatically or on a Storyboard.
I would like to show and hide the Status bar on some controllers. Can this be done or is it more of an overall app setting.
I have seen many posts/questions about the plist update:
View controller-based status bar appearance - NO
If this is completed what control is then given?
I am looking to show the status bar on the main screen of the application. But for example on a side (slide) menu I would like it not to show, is this possible? Can this be changed in IB or code?
EDIT --
I am using a https://github.com/edgecase/ECSlidingViewController implementation.
The main controller (showing the first page) should show the Status bar, but the left menu controller when it slides should not.
I believe the issue is that they both sit within the same root controller (sliding view controller) so it is difficult to complete.
Ideally if the home screen (main page) could take the status bar with it when it slides that would look best.
The plist setting "View controller-based status bar appearance" only controls if a per-controller based setting should be applied on iOS 7.
If you set this plist option to NO, you have to manually enable and disable the status bar like (as it was until iOS 6):
[[UIApplication sharedApplication] setStatusBarHidden:YES]
If you set this plist option to YES, you can add this method to each of your viewControllers to set the statusBar independently for each controller (which is esp. nice if you have a smart subclass system of viewControllers)
- (BOOL)prefersStatusBarHidden {
return YES;
}
Edit:
there are two more methods that are of interest if you are opting in the new viewController-based status bar appearance -
Force a statusbar update with:
[self setNeedsStatusBarAppearanceUpdate]
If you have nested controllers (e.g. a contentViewController in a TabBarController subclass, your TabBarController subclass might ask it's current childViewController and forward this setting. I think in your specific case that might be of use:
- (UIViewController *)childViewControllerForStatusBarHidden {
return _myChildViewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return _myOtherViewController;
}
On iOS 7 and later, just implement -prefersStatusBarHidden, for example in a UIViewController that should hide the status bar:
- (BOOL)prefersStatusBarHidden {
return YES;
}
The default is NO.
Swift 3:
override var prefersStatusBarHidden: Bool {
return true
}
You can also show/hide the status bar in an animation block, by putting animation code inside didSet property of variable that describes whether it should be shown or hidden. When you set a new value for the statusBarHidden Bool, this automatically triggers the animated updating of the status bar over the duration you have chosen.
/// Swift 3 syntax:
var statusBarHidden: Bool = true {
didSet {
UIView.animate(withDuration: 0.5) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusBarHidden = false // show statusBar, animated, by triggering didSet block
}
Swift version of Mojo66's answer:
override func prefersStatusBarHidden() -> Bool {
return true
}
I have a tab bar controller in an iPad app with 6 items. Two of the icons will when you tap at them lead to the same view controller. This view controller has a list of items it will show, it will change slightly depending on which of the tab bar icons you tapped.
How do i set this up with storyboards? Its possible to drag the relationship twice to the same view controller, but then it just shows two of the same icons on the tab bar. I want the two choices to have different icons and name.
Like you've found, you can design the view controller once in the storyboard but won't be able to associate it with the tab bar controller more than once.
What you can do is assign an identifier for it in the identity inspector, and then at runtime you can use the method -[UIStoryboard instantiateViewControllerWithIdentifier:] to instantiate a second copy of the view controller. You can insert that second copy into the tab controller using -[UITabBarController setViewControllers:animated:]
I believe you're better off creating two separate view controllers and placing them in the Tab Bar Controller individually, especially if they're going to load different data. You can place the same view controller into a Tab Bar Controller multiple times, but as far as design goes and how you can manipulate it, it won't make much sense to do it that way.
I got the same question as yours when I am trying to implement a tab bar controller view with 4 tab items, and each tab item reusing the same view controller, and the custom logic for these 4 items are set through the property when the tab bar controller call viewDidLoad. Let me show you what I have done as below:
1. Create a Tab Bar Controller with 4 view controllers
Open the storyboard, and drag a Tab Bar Controller from the object library. And drag another 2 more view controller onto the storyboard as well.
Remember to make a linkage between the tab bar controller and the 2 new view controller.
2. Create a reusing view controller for the 4 tab view
Create a new cocoa touch class, named ViewController, and create you customisation properties in the #interface section. These properties will be set in the tab bar controller viewDidLoad method, so the 4 tab views will use the same view controller, but the properties are not the same.
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
// Define your customisation properties here, so that you can set it in the tab bar controller.
#property (assign, nonatomic) UITableViewCellStyle style;
#end
4. Create a view controller subclass of UITabBarController
Create a new Cocoa Touch class, named UITabViewController, and make it subclass of UITabBarController, and conform UITabBarControllerDelegate.
For my case, I need to show a table view with different 4 cell style in 4 different tabs, so I will set the viewController's style property in the tab bar controller to achieve my target.
UITabViewController.h
#import <UIKit/UIKit.h>
#interface UITabViewController : UITabBarController <UITabBarControllerDelegate>
#end
UITabViewController.m
#import "UITabViewController.h"
#interface UITabViewController ()
#end
#implementation UITabViewController
- (void)viewDidLoad {
// Make UITabViewController as the delegate of UITabBarController
self.delegate = self;
[super viewDidLoad];
// Set-Up the UITableCell style for each tab item
[self.viewControllers enumerateObjectsUsingBlock:^(id viewController, NSUInteger idx, BOOL *stop){
UITableViewCellStyle style;
switch (idx) {
case 0:
style = UITableViewCellStyleDefault;
break;
case 1:
style = UITableViewCellStyleSubtitle;
break;
case 2:
style = UITableViewCellStyleValue1;
break;
case 3:
style = UITableViewCellStyleValue2;
break;
default:
style = UITableViewCellStyleDefault;
break;
}
SEL selector = #selector(setStyle:);
if([viewController respondsToSelector:selector]){
NSInvocation *invk = [NSInvocation invocationWithMethodSignature:[viewController methodSignatureForSelector:selector]];
[invk setSelector:selector];
[invk setTarget:viewController];
[invk setArgument:&style atIndex:2];
[invk invoke];
}
}];
}
Or if you need to set the customisation properties when the user selected different tab items, then you can add a new delegate method in the UITabViewController #implementation section:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// Check whether the view controller responds to the property setter method
SEL selector = #selector(setMyProperty:);
if([viewController respondsToSelector:selector]){
// Call the setter method with NSInvocation
NSInvocation *invk = [NSInvocation invocationWithMethodSignature:[viewController methodSignatureForSelector:selector]];
[invk setSelector:selector];
[invk setTarget:viewController];
[invk setArgument:&argument atIndex:2];
[invk invoke];
}
}
I had a similar problem and solved it quite easily.
Create a TabBarController in the Storyboard.
In each tab assign a Navigation controller.
Make all Navigation controllers set as Root View Controller the one view controller you want to share between them.
In your View Controller, in ViewDidLoad, create a condition based on the tabBarController.selectedIndex in order to customize the view controller depending on the selected tab. Something like this:
override func viewDidLoad() {
super.viewDidLoad()
if let selectedTabIndex = tabBarController?.selectedIndex {
switch selectedTabIndex:
case 0: // Customize ViewController for tab 1
case 1: // Customize ViewController for tab 2
case 2: // Customize ViewController for tab 3
default: break
}
I don't think you can do what you're trying to do with a UITabBarController. You can add two different instance of the same view controller class if you want. Otherwise, you would have to add a tab bar to a regular UIViewController, and write your own logic to switch between controllers.
I was able to accomplish this by setting the tab titles in viewDidLoad. I created 2 relationships to the same View Controller. I then set the Bar Item Title to "Not Set". Finally, I set the viewDidLoad method as follows:
UITabBarController *tc = (UITabBarController *)self.parentViewController;
NSArray *vcs = tc.viewControllers;
BOOL tabsInitialized=YES;
for (UIViewController *vc in vcs)
{
if ([vc.tabBarItem.title isEqualToString:#"Not Set"])
{
tabsInitialized = NO;
break;
}
}
if (!tabsInitialized)
{
int I=1;
for (UIViewController *vc in vcs)
{
vc.tabBarItem.title = [NSString stringWithFormat:#"Tab %i", i++];
}
}
}
I realize this question was answered a while ago, but none of the solutions mentioned using a UITabBar directly and overriding its UITabBarDelegate tabBar(:,didSelect item:) method, rather than let a UITabBarController handle the selection indirectly.
extension MyListViewController: UITabBarDelegate {
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// Set some filter based on the selected item or its tag (tag values can be assigned in IB)
switch item.tag {
// Apply filter to your datasource based on the item.tag
}
// Or if you are using a CoreData NSFetchedResultsController, set its predicate:
// resultsController.predicate = makePredicate(item.tag)
// resultsController.performFetch()
tableView.reloadData()
}
}
By overriding UITabBarDelegate methods directly, you are in direct control of the selection behavior of the tab. So you could re-use the same instance of your UIViewController instead of the UITabBarController insistently creating a new instance of the UIViewController for each tab. For example, in the didSelect method, you could apply a filter to an existing list of items in a UITableView instead of creating a new instance of the list in a new UITableView as the other solutions do.
Note: If you use a UITabBar in your view controller in code or in IB, UIKit does not seem to recognize its presence and "underlapping" scroll views (e.g. UITableView, UICollectionView) will not automatically adjust their contentInset. Nor will anchoring the bottom of the UITabBar lay itself out properly on a device with non-zero safe area margins if you anchor it to the bottom of of the safe area (it will appear to "float" with a gap between itself and the screen-edge).
Solution (part 1): Set the top constraint specially on the UITabBar instance: First anchor the bottom edge of the tab bar to the Superview and anchor the top edge to the bottom edge of the Safe Area and set the constant to 49 its (the standard height of a UITabBar as of this writing). Then it will look correct on devices with and without a safe area margin.
Solution (part 2): You will need to and manually adjust the tableView.contentInset / collectionView.contentInset to ensure content can scroll under the tab bar. You need to ensure the bottom-most item can still be scrolled up above the bottom tab bar:
Disable automatic content inset adjustment in your viewDidLoad():
tableView.contentInsetAdjustmentBehavior = .never
Adjust the UIScrollView.contentInset in an appropriate place, add the following method and call it either in viewDidLoad() or if necessary each time the view lays out in viewDidLayoutSubviews(). (If viewDidLoad() is too early for you, viewWillAppear() is probably too early, too.)
private func adjustScrollViewInsets() {
// This snippet only insets the bottom of the table view, but you may need to adjust both top and bottom edges if you also need to accommodate a top navigation bar.
var contentInset = tableView.contentInset
if let windowInsets = view.window ?? UIApplication.shared.keyWindow?.safeAreaInsets {
contentInset.bottom = windowInsets.bottom
} else {
contentInset.bottom = 0
}
tableView.contentInset = contentInset
}
Tested on SWIFT 5.x
After FAILING multiple times post :-
Implementing tabBar delegates
Setting tabBarController viewControllers array
Implementing tabBar selectedIndex switch case
Finally came some clean hack/solution to mind..
Make new UIViewController() class
Assign it to relative tabBar item
init() existing View Controller from storyboard/xib
Push without animation on viewDidLoad()
class FavouritesViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let homeViewController = storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
homeViewController.filterFavourites = true
navigationController?.pushViewController(homeViewController, animated: false)
}
}
To prevent back gesture navigation so that it won't pop to empty View Controller on left edge swipe
(OPTIONAL: Also hide navigationBar/Back button if applicable in HomeViewController)
override func viewWillAppear(_ animated: Bool) {
navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}
override func viewWillDisappear(_ animated: Bool) {
navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}