我在一个包含 6 个项目的 iPad 应用程序中有一个标签栏控制器。当您点击它们时,其中两个图标将指向同一个视图控制器。这个视图控制器有一个它将显示的项目列表,它会根据您点击的选项卡栏图标略有变化。
我如何使用故事板进行设置?可以将关系两次拖动到同一个视图控制器,但它只在选项卡栏上显示两个相同的图标。我希望这两个选项具有不同的图标和名称。
我在一个包含 6 个项目的 iPad 应用程序中有一个标签栏控制器。当您点击它们时,其中两个图标将指向同一个视图控制器。这个视图控制器有一个它将显示的项目列表,它会根据您点击的选项卡栏图标略有变化。
我如何使用故事板进行设置?可以将关系两次拖动到同一个视图控制器,但它只在选项卡栏上显示两个相同的图标。我希望这两个选项具有不同的图标和名称。
就像您发现的那样,您可以在情节提要中设计一次视图控制器,但不能多次将其与选项卡栏控制器关联。
您可以做的是在身份检查器中为其分配一个标识符,然后在运行时您可以使用方法-[UIStoryboard instantiateViewControllerWithIdentifier:]来实例化视图控制器的第二个副本。您可以使用-[UITabBarController setViewControllers:animated:]将第二个副本插入选项卡控制器
我相信你最好创建两个单独的视图控制器并将它们分别放在标签栏控制器中,特别是如果它们要加载不同的数据。您可以将同一个视图控制器多次放入选项卡栏控制器,但就设计和操作方式而言,这样做没有多大意义。
当我尝试使用4个选项卡项实现标签栏控制器视图时,我与您的选项卡栏控制器视图和重用相同视图控制器的选项卡项以及此4项的自定义逻辑时,可以通过标签栏中设置自定义逻辑控制器调用viewDidLoad
。让我向您展示我所做的如下:
1.创建一个带有4个视图控制器的Tab Bar Controller
打开情节提要,然后Tab Bar Controller
从对象库中拖出一个。并将另外 2 个view controller
拖到情节提要上。
请记住在标签栏控制器和 2 个新视图控制器之间建立链接。
2.为4个tab视图创建一个复用视图控制器
创建一个新的 cocoa touch 类,命名为ViewController
,并在 @interface 部分创建自定义属性。这些属性会在标签栏控制器viewDidLoad
方法中设置,所以4个标签视图会使用同一个视图控制器,但属性并不相同。
#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.创建UITabBarController的视图控制器子类
创建一个新的 Cocoa Touch 类,命名为UITabViewController
,并使其成为 的子类UITabBarController
,并符合UITabBarControllerDelegate
。
对于我的情况,我需要在 4 个不同的选项卡中显示具有不同 4 单元格样式的表格视图,因此我将在选项卡栏控制器中设置viewController
'style
属性以实现我的目标。
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];
}
}];
}
或者,如果您需要在用户选择不同的选项卡项时设置自定义属性,那么您可以在UITabViewController
@implementation 部分添加一个新的委托方法:
- (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];
}
}
我有一个类似的问题,很容易解决。
在情节提要中创建一个 TabBarController。
在每个选项卡中分配一个导航控制器。
将所有导航控制器设置为根视图控制器,这是您要在它们之间共享的一个视图控制器。
在您的视图控制器中,在 ViewDidLoad 中,基于 tabBarController.selectedIndex 创建一个条件,以便根据所选选项卡自定义视图控制器。像这样的东西:
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
}
我不认为你可以用 UITabBarController 做你想做的事情。如果需要,您可以添加同一视图控制器类的两个不同实例。否则,您将不得不向常规 UIViewController 添加一个标签栏,并编写您自己的逻辑来在控制器之间切换。
我可以通过在 viewDidLoad 中设置标签标题来实现这一点。我为同一个视图控制器创建了 2 个关系。然后我将栏项目标题设置为“未设置”。最后,我设置 viewDidLoad 方法如下:
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++];
}
}
}
我意识到这个问题在不久前就得到了回答,但是没有提到UITabBar
直接使用 a 并覆盖其UITabBarDelegate
tabBar(:,didSelect item:)
方法的解决方案,而不是让 aUITabBarController
间接处理选择。
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()
}
}
通过直接覆盖UITabBarDelegate
方法,您可以直接控制选项卡的选择行为。因此,您可以重复使用您的相同实例,UIViewController
而不是UITabBarController
坚持UIViewController
为每个选项卡创建一个新实例。例如,在该didSelect
方法中,您可以将过滤器应用于 a 中的现有项目列表,而不是像其他解决方案那样UITableView
在 new 中创建列表的新实例。UITableView
注意:如果您UITabBar
在代码或 IB 中的视图控制器中使用 a,UIKit 似乎无法识别它的存在,并且“重叠”滚动视图(例如UITableView
,UICollectionView
)不会自动调整它们的contentInset
. 如果您将布局的底部锚定到安全区域的底部,也不会将UITabBar
布局的底部正确地锚定在具有非零安全区域边距的设备上(它看起来会“浮动”,其自身与屏幕之间存在间隙 -边缘)。
解决方案(第1部分):在实例上专门设置顶部约束UITabBar
:首先将标签栏的底部边缘锚定到安全区域的底部边缘并将顶部边缘Superview
锚定到安全区域的底部边缘并将常量设置为49(标准高度)在撰写本文时,UITabBar 的名称)。然后它在有和没有安全区域边距的设备上看起来都是正确的。
解决方案(第 2 部分):您需要手动调整tableView.contentInset
/collectionView.contentInset
以确保内容可以在标签栏下滚动。您需要确保最底部的项目仍然可以在底部标签栏上方向上滚动:
在您的中禁用自动内容插入调整viewDidLoad()
:
tableView.contentInsetAdjustmentBehavior = .never
UIScrollView.contentInset
在适当的位置调整,添加以下方法并在viewDidLoad()
每次视图布局时调用它或在必要时调用它viewDidLayoutSubviews()
。(如果viewDidLoad()
对你来说太早了,viewWillAppear()
可能也太早了。)
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
}