Customizing the More menu on a Tab bar

后端 未结 6 720
野性不改
野性不改 2020-11-27 11:58

I am using a tab bar (UITabBarController) on my app and I wish to customize the appearance of the table that appears when you click the more button. I have worked out how to

相关标签:
6条回答
  • 2020-11-27 12:13

    Following on from Stephan's suggestion to replace the dataSource of the moreNavigationController, here is a quick over view of the code I implemented.

    I created a new class called MoreTableViewDataSource which implements the UITableViewDataSource protocol. The controller which the more page actually uses to build the table is called the UIMoreListControllerModern, and this implements just the required parts of the UITableViewDataSource protocol. My implementation looks like this.

    -(MoreTableViewDataSource *) initWithDataSource:(id<UITableViewDataSource>) dataSource
    {
        self = [super init];
        if (self)
        {
                self.originalDataSource = dataSource;
        }
    
        return self;
    }
    
    - (void)dealloc
    {
        self.originalDataSource = nil;
        [super dealloc];
    }
    
    - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
    {
        return [originalDataSource tableView:table numberOfRowsInSection:section];
    }
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [originalDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
        cell.textColor = [UIColor whiteColor];
        return cell;
    }
    

    and then in my CustomTabBarController class I override viewDidLoad as follows:

    - (void)viewDidLoad {
    
        [super viewDidLoad];
    
        UINavigationController *moreController = self.moreNavigationController;
        moreController.navigationBar.barStyle = UIBarStyleBlackOpaque;
    
        if ([moreController.topViewController.view isKindOfClass:[UITableView class]])
        {
    
            UITableView *view = (UITableView *)moreController.topViewController.view;
            view.backgroundColor = [UIColor blackColor];
            moreTableViewDataSource = [[MoreTableViewDataSource alloc] initWithDataSource:view.dataSource];
            view.dataSource = moreTableViewDataSource;
    
        }
    }
    

    As requested here are the header files

    @interface MoreTableViewDataSource : NSObject <UITableViewDataSource>
    {
        id<UITableViewDataSource> originalDataSource;
    }
    
    @property (retain) id<UITableViewDataSource> originalDataSource;
    
    -(MoreTableViewDataSource *) initWithDataSource:(id<UITableViewDataSource>) dataSource;
    
    @end
    

    and

    #import "MoreTableViewDataSource.h"
    
    @interface CustomTabBarController : UITabBarController 
    {
        MoreTableViewDataSource *moreTableViewDataSource;
    }
    
    0 讨论(0)
  • 2020-11-27 12:14

    I followed Ian's implementation to customize the More menu, but I was having a problem retaining the customizations after a memory warning. didReceiveMemoryWarning seems to destroy the UITableView, and when it is regenerated it gets its old dataSource back. Here's my solution:

    I replace viewDidLoad on the CustomTabBarController with this:

    - (void)viewDidLoad {
        [super viewDidLoad];
        UINavigationController* moreController = self.moreNavigationController;
        if ([moreController.topViewController.view isKindOfClass:[UITableView class]]) {
            moreController.delegate = self;
            self.moreControllerClass = [moreController.topViewController class];
            UITableView* view = (UITableView*) moreController.topViewController.view;
            self.newDataSource = [[[MoreDataSource alloc] initWithDataSource:view.dataSource] autorelease];
        }
    }
    

    As you can see, I added a few properties for storing things I needed. Those have to be added to the header and synthesized. I also made CustomTabBarController a UINavigationControllerDelegate in the header. Here's the delegate function I added:

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
        if ([viewController isKindOfClass:self.moreControllerClass]) {
            UIView* view = self.moreNavigationController.topViewController.view;
            if ([view isKindOfClass:[UITableView class]]) {
                UITableView* tview = (UITableView*) view;
                tview.dataSource = self.newDataSource;
                tview.rowHeight = 81.0;
            }
        }
    }
    

    This way I make sure my custom data source is always used, because I set it that way just prior to showing the UIMoreListController, every time it's shown.

    0 讨论(0)
  • 2020-11-27 12:27

    This works for me in iOS 13, Swift 5.1:

    extension MyTabBarController: UITabBarControllerDelegate {
        // handle a select of the More tab
        func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
            // style all the tab bar windows and the More tab bar tableview
            if viewController == moreNavigationController,
                let moreTableView = moreNavigationController.topViewController?.view as? UITableView {
                view.tintColor = .systemOrange
                moreNavigationController.navigationBar.tintColor = .systemOrange
                moreTableView.tintColor = .systemOrange
                moreTableView.backgroundColor = UIColor(named: "Your Color")
                moreTableView.visibleCells.forEach {
                    $0.backgroundColor = UIColor(named: "Your Color")
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 12:28
    @interface TabBarViewController () <UITableViewDelegate,UITableViewDataSource>
    
    @property (nonatomic,strong) UITableView* tabBarTableView;
    @property (nonatomic,weak) id <UITableViewDelegate> currentTableViewDelegate;
    
    @end
    
    @implementation TabBarViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self costumizeMoreTableView];
    
    }
    
    -(void)costumizeMoreTableView{
        _tabBarTableView = (UITableView *)self.moreNavigationController.topViewController.view;
        _currentTableViewDelegate = _tabBarTableView.delegate;
        _tabBarTableView.delegate = self;
        _tabBarTableView.dataSource = self;
        [_tabBarTableView registerNib:[UINib nibWithNibName:@"MoreTabBarTableViewCell" bundle:nil] forCellReuseIdentifier:@"MoreTabBarTableViewCell"];
    
    }
    
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
        return 120;
    }
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
        return 2;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        MoreTabBarTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MoreTabBarTableViewCell" forIndexPath:indexPath];
        [cell setMoreTableValues];
        return cell;
    }
    
    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath{
          [_currentTableViewDelegate tableView:tableView didSelectRowAtIndexPath:indexPath];
     }
    
     @end
    
    0 讨论(0)
  • 2020-11-27 12:34

    visibleCells is populated only after the moreNavigationController is displayed.

    And the cells are created at runtime, so even if you change the content of the cells, they are replaced when they are displayed.

    One thing to try would be to replace the datasource of the moreNavigationController tableView, call the cellForRowAtIndexPath of the original datasource and change its content before returning it.

    Using the code below, after having displayed once the moreNavigationController to initialize it, you'll see that when you return to the moreNavigationController, the cells are red, but return immediately to white background.

    UITableView *view = (UITableView *)self.tabBarController.moreNavigationController.topViewController.view;
    if ([[view subviews] count]) {
      for (UITableViewCell *cell in [view visibleCells]) {
        cell.backgroundColor = [UIColor redColor];
      }
    }
    
    0 讨论(0)
  • 2020-11-27 12:34

    Thanks to Unknown. Following his solution, I will put his code in Swift. Only what you should do more is create MoreTableViewCell class and just it. You don't have to use Storyboard. If you want to modify tableView you can do it in customizeMoreTableView method.

    class TabBarMenuController: UITabBarController, UITableViewDelegate, UITableViewDataSource{
    
    var tabBarItems: [UIViewController] = []
    var areMessagesVisible: Bool = false
    
    var titleForTabBars: [String] = ["resources", "events", "training", "my profile", "news", "contacts"]
    var iconNames: [String] = ["film", "calendar", "classroom", "profile", "news", "Phone"]
    var controllersStoryboardId: [String] = ["resourcesNavController", "eventsNavController", "enablementNavController", "profileNavController", "newsNavController", "contactsNavController"]
    
    // to manage moreTableView
    var moreTableView: UITableView = UITableView()
    var currentTableViewDelegate: UITableViewDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.customizeMoreTableView()
        //to REMOVE
        areMessagesVisible = true
    
        if !areMessagesVisible{
            self.titleForTabBars.removeAtIndex(4)
            self.controllersStoryboardId.removeAtIndex(4)
            self.iconNames.removeAtIndex(4)
        }
    
        for i in 0 ..< controllersStoryboardId.count{
            tabBarItems.append(UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier(controllersStoryboardId[i]) as? UINavigationController ?? UINavigationController())
        }
        self.moreNavigationController.navigationBar.tintColor = UIColor.blackColor()
    }
    
    override func viewWillAppear(animated: Bool) {
    
        for i in 0 ..< tabBarItems.count{
            tabBarItems[i].tabBarItem = UITabBarItem(title: titleForTabBars[i], image: UIImage(named: iconNames[i]), selectedImage: UIImage(named: iconNames[i]))
        }
        self.viewControllers = tabBarItems
    }
    
    func customizeMoreTableView(){
        moreTableView = self.moreNavigationController.topViewController!.view as? UITableView ?? UITableView()
        currentTableViewDelegate = moreTableView.delegate;
        moreTableView.delegate = self
        moreTableView.dataSource = self;
        moreTableView.registerClass(MoreTableViewCell.self, forCellReuseIdentifier: "MoreTableViewCell")
    
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
        let moreCell  = tableView.dequeueReusableCellWithIdentifier("MoreTableViewCell", forIndexPath: indexPath) as? MoreTableViewCell ?? MoreTableViewCell()
    
        moreCell.textLabel?.text = titleForTabBars[indexPath.row + 4]
        moreCell.imageView?.image = UIImage(named: iconNames[indexPath.row + 4])
    
        /*let testLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 40))
        testLabel.backgroundColor = UIColor.yellowColor()
        moreCell.addSubview(testLabel)
        */
        return moreCell
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return titleForTabBars.count - 4
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        currentTableViewDelegate?.tableView!(tableView, didSelectRowAtIndexPath: indexPath)
    }
    
     }
    
    0 讨论(0)
提交回复
热议问题