Практическое руководство. Сохранение порядка вкладок при настройке вкладок в UITabBarController

У меня возникают проблемы с поиском какой-либо другой информации, кроме документов, как сохранить порядок вкладок для моего UITabBarController, чтобы пользовательская настройка была сохранена для следующего запуска приложения. Я искал в Интернете, но не смог найти никаких сообщений в блоге или статей, которые проходят через соответствующий код для этого.

Я понимаю, что мне нужно использовать методы делегата для UITabBarController (didEndCustomizingViewControllers:), но я не уверен, как наилучшим образом подойти к сохранению состояния заказа, в котором пользователь хочет видеть вкладки.

Может кто-нибудь отправить какой-то код, указать мне в правильном направлении или, возможно, у вас есть ссылка на что-то сохраненное?:)

Спасибо

Ответ 1

Насколько вы просили какой-то образец кода, я просто отправлю здесь, как я имел дело с одной и той же задачей в своем приложении.

Быстрое введение: я использовал файл NIB для хранения начального состояния UITabBarController и отличался от своих вкладок один от другого. Я просто определял переменные тега для UITabBarItem объектов, назначенных каждому UIViewController, набитым в моем UITabBarController. Чтобы иметь возможность точно отслеживать последнюю выбранную вкладку (включая "Больше" ), я применил следующие методы для UITabBarControllerDelegate моих UITabBarController и UINavigationControllerDelegate своего moreNavigationController. Вот они:

#pragma mark UINavigationControllerDelegate

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [[NSUserDefaults standardUserDefaults] setInteger:mainTabBarController.selectedIndex forKey:@"mainTabBarControllerSelectedIndex"];
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [[NSUserDefaults standardUserDefaults] setInteger:mainTabBarController.selectedIndex forKey:@"mainTabBarControllerSelectedIndex"];
}

#pragma mark UITabBarControllerDelegate

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
    [[NSUserDefaults standardUserDefaults] setInteger:tabBarController.selectedIndex forKey:@"mainTabBarControllerSelectedIndex"];
}

И вот код для сохранения порядка вкладок:

#pragma mark UITabBarControllerDelegate

- (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray *)viewControllers changed:(BOOL)changed {
    int count = mainTabBarController.viewControllers.count;
    NSMutableArray *savedTabsOrderArray = [[NSMutableArray alloc] initWithCapacity:count];
    for (int i = 0; i < count; i ++) {
        [savedTabsOrderArray addObject:[NSNumber numberWithInt:[[[mainTabBarController.viewControllers objectAtIndex:i] tabBarItem] tag]]];
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:savedTabsOrderArray] forKey:@"tabBarTabsOrder"];
    [savedTabsOrderArray release];
}

Как вы можете видеть, я сохранял порядок индексов вкладок в массиве в NSUserDefaults.

При запуске приложения в applicationDidFinishLaunching: я переупорядочил UIViewControllers, используя следующий код:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    mainTabBarController.delegate = self;

    int count = mainTabBarController.viewControllers.count;
    NSArray *savedTabsOrderArray = [[userDefaults arrayForKey:@"tabBarTabsOrder"] retain];
    if (savedTabsOrderArray.count == count) {
        BOOL needsReordering = NO;

        NSMutableDictionary *tabsOrderDictionary = [[NSMutableDictionary alloc] initWithCapacity:count];
        for (int i = 0; i < count; i ++) {
            NSNumber *tag = [[NSNumber alloc] initWithInt:[[[mainTabBarController.viewControllers objectAtIndex:i] tabBarItem] tag]];
            [tabsOrderDictionary setObject:[NSNumber numberWithInt:i] forKey:[tag stringValue]];

        if (!needsReordering && ![(NSNumber *)[savedTabsOrderArray objectAtIndex:i] isEqualToNumber:tag]) {
                needsReordering = YES;
            }
        }

        if (needsReordering) {
            NSMutableArray *tabsViewControllers = [[NSMutableArray alloc] initWithCapacity:count];
            for (int i = 0; i < count; i ++) {
                [tabsViewControllers addObject:[mainTabBarController.viewControllers objectAtIndex:
                                                [(NSNumber *)[tabsOrderDictionary objectForKey:
                                                              [(NSNumber *)[savedTabsOrderArray objectAtIndex:i] stringValue]] intValue]]];
            }
            [tabsOrderDictionary release];

            mainTabBarController.viewControllers = [NSArray arrayWithArray:tabsViewControllers];
            [tabsViewControllers release];
        }
    }
    [savedTabsOrderArray release];

    if ([userDefaults integerForKey:@"mainTabBarControllerSelectedIndex"]) {
        if ([userDefaults integerForKey:@"mainTabBarControllerSelectedIndex"] == 2147483647) {
            mainTabBarController.selectedViewController = mainTabBarController.moreNavigationController;
        }
        else {
            mainTabBarController.selectedIndex = [userDefaults integerForKey:@"mainTabBarControllerSelectedIndex"];
        }
    }

    mainTabBarController.moreNavigationController.delegate = self;

    [window addSubview:mainTabBarController.view];
}

Это довольно сложно и может показаться странным, но не забывайте, что мой UITabBarController был полностью создан в файле nib. Если вы его программно создаете, вы можете просто сделать то же самое, но следовать сохраненному порядку.

P.S.: и не забудьте синхронизировать NSUserDefaults, когда ваше приложение завершается.

- (void)applicationWillTerminate:(UIApplication *)application {
    [[NSUserDefaults standardUserDefaults] synchronize];
}

Надеюсь, это поможет. Если что-то неясно, прокомментируйте и спросите.

Ответ 2

Сначала я проголосовал за предыдущий ответ, но потом я заметил, насколько это смехотворно сложно. Он может и должен быть упрощен.

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    NSArray *initialViewControllers = [NSArray arrayWithArray:self.tabBarController.viewControllers];
    NSArray *tabBarOrder = [[AppDelegate sharedSettingsService] tabBarOrder];
    if (tabBarOrder) {
        NSMutableArray *newViewControllers = [NSMutableArray arrayWithCapacity:initialViewControllers.count];
        for (NSNumber *tabBarNumber in tabBarOrder) {
            NSUInteger tabBarIndex = [tabBarNumber unsignedIntegerValue];
            [newViewControllers addObject:[initialViewControllers objectAtIndex:tabBarIndex]];
        }
        self.tabBarController.viewControllers = newViewControllers;
    }

    NSInteger tabBarSelectedIndex = [[AppDelegate sharedSettingsService] tabBarSelectedIndex];
    if (NSIntegerMax == tabBarSelectedIndex) {
        self.tabBarController.selectedViewController = self.tabBarController.moreNavigationController;
    } else {
        self.tabBarController.selectedIndex = tabBarSelectedIndex;
    }

    /* Add the tab bar controller current view as a subview of the window. */
    [self.window addSubview:self.tabBarController.view];
}

- (void)applicationWillTerminate:(UIApplication *)application {

    NSInteger tabBarSelectedIndex = self.tabBarController.selectedIndex;
    [[AppDelegate sharedSettingsService] setTabBarSelectedIndex:tabBarSelectedIndex];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray *)viewControllers changed:(BOOL)changed {

        NSUInteger count = tabBarController.viewControllers.count;
        NSMutableArray *tabOrderArray = [[NSMutableArray alloc] initWithCapacity:count];
        for (UIViewController *viewController in viewControllers) {

            NSInteger tag = viewController.tabBarItem.tag;
            [tabOrderArray addObject:[NSNumber numberWithInteger:tag]];
        }

        [[AppDelegate sharedSettingsService] setTabBarOrder:[NSArray arrayWithArray:tabOrderArray]];
        [tabOrderArray release];
    }

Все это происходит в AppDelegate. Вы устанавливаете делегат UITabBarController экземпляру AppDelegate в Interface Builder. sharedSettingsService - это то, что сохраняет данные для меня. В основном это может быть интерфейс NSUserDefaults или все, что вам нравится (например, CoreData). Итак, все просто, Interface Builder помогает здесь, а не делает вещи более сложными.

Ответ 3

Возможно, поздно в игре, но изучал Свифта менее двух месяцев в школе и сидел, возможно, более пятнадцати часов с этим, потому что я не мог найти достойного объяснения в interwebz.

Вот решение в Swift

  • Дайте все ваши теги tabItem, начиная с 1. Вы делаете это в каждом отдельном представлении. Если у вас есть шесть просмотров, их вкладки будут иметь в этом случае уникальный номер в диапазоне от 1 до 6.

  • Добавьте UITabBarControllerDelegate ко всем классам ViewControllers, чтобы вы могли использовать функцию, описанную ниже в пункте 5.

    class FirstViewController: UIViewController, UITabBarControllerDelegate {
    
  • Добавьте следующую переменную глобально (сразу после кода выше, в качестве примера), чтобы вы могли сохранять переменные локально на телефоне из любой функции внутри класса.

    let defaults = NSUserDefaults.standardUserDefaults()
    
  • Делегируйте tabBarController в представление, чтобы представление могло обновлять любые изменения в tabBarController. Поместите следующее в свой viewDidLoad().

    tabBarController!.delegate = self
    
  • Внедрите следующий код. Этот режим активируется, когда пользователь редактирует представление вкладки. Что делает код, он принимает теги [ViewControllers] в том порядке, в котором они находятся (после того, как пользователь изменил его) и сохраняет его локально на телефоне. Первый тег viewController сохраняется как целое число в переменной "0", второй тег в переменной "1" и т.д.

    func tabBarController(tabBarController: UITabBarController, didEndCustomizingViewControllers viewControllers: [UIViewController], changed: Bool) {
        if (changed) {
            print("New tab order:")
            for (var i=0; i<viewControllers.count; i++) {
                defaults.setInteger(viewControllers[i].tabBarItem.tag, forKey: String(i))
                print("\(i): \(viewControllers[i].tabBarItem.title!) (\(viewControllers[i].tabBarItem.tag))")
            }
        }
    }
    

Отпечатки скажут вам новый порядок вкладок. Ничего вам не понадобится, но я думаю, что приятно видеть, что происходит в фоновом режиме. Все это было только для сохранения порядка вкладок. Теперь вам нужно будет восстановить их при запуске программы.

  1. Переключитесь с вашего файла UIViewControl на AppDelegate.swift.

  2. Наконец, напишите следующее в приложении func (приложение: UIApplication, didFinishLaunchingWithOptions..., которое вы можете найти в верхней части AppDelegate.swift.

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Let you read and write to local variables on your phone
    let defaults = NSUserDefaults.standardUserDefaults()
    
    // Getting access to your tabBarController
    let tabBar: UITabBarController = self.window?.rootViewController as! UITabBarController
    
    var junkViewControllers = [UIViewController]()
    
    // returns 0 if not set, hence having the tabItem tags starting at 1.
    var tagNumber : Int = defaults.integerForKey("0")
    
    if (tagNumber != 0) {
        for (var i=0; i<tabBar.viewControllers?.count; i++) {
            // the tags are between 1-6 but the order of the
            // viewControllers in the array are between 0-5
            // hence the "-1" below.
            tagNumber = defaults.integerForKey( String(i) ) - 1
            junkViewControllers.append(tabBar.viewControllers![tagNumber])
        }
    
        tabBar.viewControllers = junkViewControllers
    }
    }
    

Хорошо знать, что все представления, которые содержит вкладка tabBarController, хранятся в виде массива в tabBarController.viewControllers.

Этот код в основном создает массив, называемый junkViewControllers. Затем for-loop добавляет существующие UIViewControllers из программы в порядке от предыдущих сохраненных переменных на основе тегов UIViewControllers. Когда все это будет сделано, tabBarController, сокращенный до tabBar, будет перезаписан массивом junkViewController.

Ответ 4

Я объясню, как это сделать программно. ПРИМЕЧАНИЕ. Это использование ARC, поэтому вам, возможно, придется вставлять вызовы сохранения/освобождения по мере необходимости.

Для сортировки используется свойство tag для UITabBarItem. Для каждого UIViewController, который вы добавляете в UITabBarController, убедитесь, что у каждого есть уникальный tag.

- (id)init
{
    self = [super init];
    if (self) {
        self.tabBarItem.tag = 0;
        self.tabBarItem.image = <image>;
        self.tabBarItem.title = <title>;
    }
    return self;
}

Предположительно, вы просто будете использовать свой порядок сортировки по умолчанию для своих тегов, поэтому все, что у вас есть в качестве исходного первого контроллера представления, будет 0, затем 1, 2, 3 и т.д.

Настройте свои UIViewControllers в AppDelegate didFinishLaunchingWithOptions, как обычно, убедитесь, что вы создаете их в своем "порядке по умолчанию". По мере этого добавьте их в экземпляр NSMutableArray.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{    
    self.tabBarController = [[UITabBarController alloc] init];
    self.tabBarController.delegate = self;

    NSMutableArray *unsortedControllers = [NSMutableArray array];

    UIViewController *viewOne = [[UIViewController alloc] init];
    [unsortedControllers addObject:viewOne];

    UIViewController *viewTwo = [[UIViewController alloc] init];
    [unsortedControllers addObject:viewTwo];
    ...

После того, как все они будут созданы и добавлены в массив, вы проверите, выполнил ли пользователь свой заказ, запросив NSUserDefaults. В значениях по умолчанию вы будете хранить массив пользовательского настраиваемого порядка вкладок. Это будет массив NSNumbers (как это создается, объясняется в последнем фрагменте кода). Используйте их для создания нового "отсортированного" массива контроллеров представлений и передайте его контроллеру панели вкладок. Если они не настроили заказ, значение по умолчанию будет возвращать нуль, и вы можете просто использовать несортированный массив.

    ...
    NSArray *tabBarOrder = [[NSUserDefaults standardUserDefaults] arrayForKey:@"tabBarOrder"];
    if (tabBarOrder)
    {
      NSMutableArray *sortedControllers = [NSMutableArray array];
      for (NSNumber *sortNumber in tabBarOrder)
      {
         [sortedControllers addObject:[unsortedControllers objectAtIndex:[sortNumber intValue]]];
      }
      self.tabBarController.viewControllers = sortedControllers;
    } else {
      self.tabBarController.viewControllers = unsortedControllers;
    }
    [self.window setRootViewController:self.tabBarController];
    [self.window makeKeyAndVisible];

    return YES;
}

Чтобы создать настраиваемый порядок сортировки, используйте метод делегата UITabBarController:

- (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray *)viewControllers changed:(BOOL)changed
{
    NSMutableArray *tabOrderArray = [[NSMutableArray alloc] init];
    for (UIViewController *vc in self.tabBarController.viewControllers)
    {
        [tabOrderArray addObject:[NSNumber numberWithInt:[[vc tabBarItem] tag]]];
    }
    [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:tabOrderArray] forKey:@"tabBarOrder"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

Ответ 5

Вот что помогло, основываясь на ответах Рикарда и Йеспера. Весь код входит в основной TabBarController.

import UIKit

class TabBarController: UITabBarController, UITabBarControllerDelegate {

let tabOrderKey = "customTabBarOrder"

override func viewDidLoad() {
    super.viewDidLoad()

    self.delegate = self
    loadCustomTabOrder()
}

func loadCustomTabOrder() {

    let defaults = NSUserDefaults.standardUserDefaults()
    let standardOrderChanged = defaults.boolForKey(tabOrderKey)

    if standardOrderChanged {
        print("Standard Order has changed")

        var VCArray = [UIViewController]()
        var tagNumber = 0

        let tabBar = self as UITabBarController

        if let countVC = tabBar.viewControllers?.count {
            print("\(countVC) VCs in total")
            for var x = 0; x < countVC; x++ {
                tagNumber = defaults.integerForKey("tabPosition\(x)")

                for VC in tabBar.viewControllers! {
                    if tagNumber == VC.tabBarItem.tag {
                        VCArray.append(VC)
                        print("Position \(x): \(VCArray[x].tabBarItem.title!) VC (tag \(tagNumber))")
                    }
                }
            }
        }
        tabBar.viewControllers = VCArray
    }

}

func tabBarController(tabBarController: UITabBarController, didEndCustomizingViewControllers viewControllers: [UIViewController], changed: Bool) {

    print("Change func called")

    if changed {
        print("Order has changed")
        let defaults = NSUserDefaults.standardUserDefaults()

        for var x = 0; x < viewControllers.count; x++ {
            defaults.setInteger(viewControllers[x].tabBarItem.tag, forKey: "tabPosition\(x)")
            print("\(viewControllers[x].tabBarItem.title!) VC (with tag: \(viewControllers[x].tabBarItem.tag)) is now in position \(x)")
        }
        defaults.setBool(true, forKey: tabOrderKey)
    } else {
        print("Nothing has changed")
    }
}

}

Ответ 6

Упрощенный Рикард Элимяэ ответит еще дальше. "Быстрое" решение для сохранения и загрузки настраиваемых ViewControllers с использованием функции-делегата tabBarController CustomizingViewControllers.

Вот как я это сделал.

class TabBarController: UITabBarController, UITabBarControllerDelegate {

    let kOrder = "customOrder"

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self

        loadCustomizedViews()
    }

    func loadCustomizedViews(){
        let defaults = NSUserDefaults.standardUserDefaults()

        // returns 0 if not set, hence having the tabItem tags starting at 1.
        let changed : Bool = defaults.boolForKey(kOrder)

        if changed {
            var customViewControllers = [UIViewController]()
            var tagNumber: Int = 0
            for (var i=0; i<self.viewControllers?.count; i++) {
                // the tags are between 0-6 and the
                // viewControllers in the array are between 0-6
                // so we swap them to match the custom order
                tagNumber = defaults.integerForKey( String(i) )

                //print("TabBar re arrange i = \(i), tagNumber = \(tagNumber),  viewControllers.count = \(self.viewControllers?.count) ")
                customViewControllers.append(self.viewControllers![tagNumber])
            }

            self.viewControllers = customViewControllers
        }
    }

    func tabBarController(tabBarController: UITabBarController, didEndCustomizingViewControllers viewControllers: [UIViewController], changed: Bool){

        if (changed) {
            let defaults = NSUserDefaults.standardUserDefaults()
            //print("New tab order:")
            for (var i=0; i<viewControllers.count; i++) {
                defaults.setInteger(viewControllers[i].tabBarItem.tag, forKey: String(i))
                //print("\(i): \(viewControllers[i].tabBarItem.title!) (\(viewControllers[i].tabBarItem.tag))")
            }

            defaults.setBool(changed, forKey: kOrder)
        }
    }
}

Ответ 7

Обновлен ответ для Swift 2.0

`
  let tabBarOrderKey = "tabBarOrderKey"

extension TabBarButtonsController: UITabBarControllerDelegate {

 // Saves new tab bar custom order

  func tabBarController(tabBarController: UITabBarController, didEndCustomizingViewControllers viewControllers: [UIViewController], changed: Bool) {
var orderedTagItems = [Int]()
if changed {
  for viewController in viewControllers {
    let tag = viewController.tabBarItem.tag
    orderedTagItems.append(tag)

  }
  NSUserDefaults.standardUserDefaults().setObject(orderedTagItems, forKey: tabBarOrderKey)
}
}

// set up tag to compare with when pulling from defaults and for saving initial tab bar change

func setUpTabBarItemTags() {
var tag = 0
if let viewControllers = viewControllers {
  for view in viewControllers {
    view.tabBarItem.tag = tag
    tag += 1
  }
 }
}

// Get Saved Tab Bar Order from defaults

func getSavedTabBarItemsOrder() {
var newViewControllerOrder = [UIViewController]()
if let initialViewControllers = viewControllers {
  if let tabBarOrder = NSUserDefaults.standardUserDefaults().objectForKey(tabBarOrderKey) as? [Int] {
    for tag in tabBarOrder {
      newViewControllerOrder.append(initialViewControllers[tag])
    }
    setViewControllers(newViewControllerOrder, animated: false)
  }
 }
}

}

`

Не забудьте установить делегат и вызвать эти методы в представлении, загрузив