Как использовать общий целевой объект для обработки действий / выходов нескольких представлений? - PullRequest
2 голосов
/ 05 августа 2011

Я начинаю пытаться использовать IB, где ранее я использовал код. Я создал перья для iPad / iPhone Portrait / Landscape, поэтому у меня есть четыре возможных вида. Всем владельцам файлов присвоено значение UIViewController, у всех есть объект, добавленный из палитры, установленной в мой подкласс NSObject "TargetObject". Нет проблем с установкой IBActions / IBOutlets, просто перетащите Ctrl, как обычно, чтобы создать код в одном наконечнике, затем щелкните правой кнопкой мыши объект и перетащите действия / выходы в правые элементы управления в других наконечниках, чтобы соединение существовало во всех крупка. Идея состоит в том, что делегат приложения запускает соответствующий контроллер представления для текущего устройства, а затем этот контроллер представления загружает правильное представление в зависимости от ориентации.

(Может быть, при использовании мастерства автоматического изменения размера этот подход не нужен - я хочу знать, возможно ли сделать это таким образом, потому что расположение макетов для правильной перестройки только с помощью этих элементов управления IB очень, очень расстраивает.)

Идея этих представлений типа «один ко многим» и целевых объектов заключается в том, что я могу использовать их в любом месте приложения. Например, где у меня отображается что-то, что является объектом в основных данных и я хотел бы разрешить подробный просмотр / редактирование в нескольких местах в приложении. Похоже, очень MVC способ делать вещи.

Проблема возникает в loadView контроллера представления, когда я делаю:

NSArray *portraitViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Portrait" owner:self options:nil];
portraitWeekView = [portraitViewArray objectAtIndex:0];

NSArray *landscapeViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Landscape" owner:self options:nil];
landscapeWeekView = [landscapeViewArray objectAtIndex:0];

// this object is intended to be a common target for portrait and landscape arrays. 
weekViewTarget = [portraitViewArray objectAtIndex:1];

UIInterfaceOrientation interfaceOrientation = self.interfaceOrientation;

if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
    self.view = portraitWeekView;
else 
    self.view = landscapeWeekView;

Как вы уже догадались, в портретной ориентации все нормально, но в альбомной среде любое взаимодействие с целевым объектом падает. Объект weekViewTarget ссылается на объект, созданный в книжной ориентации, поэтому альбомная ориентация ищет другой экземпляр, который не был сохранен со ссылкой. В альбомной ориентации _viewDelegate имеет значение ноль - это частное, поэтому я не могу исправить это в коде.

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

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

Как правильно создать несколько представлений, реализованных в виде перьев, с использованием общего целевого объекта для работы с моделью?

  • прогресс - все начинает работать, если я назначу каждой цели указатель на свой тип:

    WeekViewTarget *forwardTarget;
    

и пусть реальный обработчик forwardTarget = nil, в то время как forwardTarget неиспользуемого обработчика устанавливается следующим образом:

weekViewTarget = [portraitViewArray objectAtIndex:1];
forwardedWeekViewTarget = [landscapeViewArray objectAtIndex:1];
forwardedWeekViewTarget.forwardTarget = weekViewTarget;

, а затем выполните следующие действия в каждом IBAction:

- (IBAction)timeBlockTapped:(id)sender {

    if(forwardTarget != nil) {
        [forwardTarget performSelector:@selector(timeBlockTapped:) withObject:sender];
    } else {
        NSLog(@"Got a tap event with sender %@", [sender description]);
    }
}

Все еще не чувствует себя хорошо, хотя.

Ответы [ 2 ]

17 голосов
/ 05 августа 2011

Адам, звучит так, будто вам нужен прокси-объект (он же внешний).

Ваша проблема

NSBundle и UINib возвращают автоматически освобожденные массивы при создании экземпляров графов объектов из NIB.Любой объект, который вы специально не сохраняете, будет освобожден, когда массив будет освобожден в конце цикла событий.Очевидно, что объекты внутри пера, которые ссылаются друг на друга, будут сохраняться до тех пор, пока сохраняется их владелец.

Таким образом, ваш второй экземпляр целевого объекта действия освобождается, потому что вы не держитесь за него.

Что вам действительно нужно, так это всего лишь один экземпляр целевого объекта, на который могут ссылаться оба NIB.Таким образом, вы не можете иметь свой NIB для создания экземпляра этого объекта.Но ваш NIB должен иметь возможность ссылаться на этот экземпляр объекта!

О, дорогой, что делать?!

Решение: Прокси / Внешние объекты

По сути, внешний объект - это прокси, который вы помещаете в NIB.Затем вы вводите фактический экземпляр, когда вы создаете граф объектов NIB.Загрузчик NIB заменяет прокси в NIB на предоставленный извне экземпляр и настраивает любые цели или выходы, назначенные для прокси в Интерфейсном Разработчике.

Как это сделать

  1. В Интерфейсном Разработчике перетащите в «Внешний объект», а не в экземпляр NSObject.Он появится в верхнем разделе вместе с владельцем файла в Xcode 4.x.
  2. Выберите его и в «Identity Inspector» установите его класс соответствующего типа.
  3. В «Атрибутах»Инспектор "устанавливает строку идентификатора в" TargetObject "(или любую строку, которую вы хотите использовать в приведенном ниже коде).
  4. Прибыль!

Код

id owner = self; // or whatever
id targetObject = self.targetObject; // or whatever...

// Construct a dictionary of objects with strings identifying them.  
// These strings should match the Identifiers you specified in IB's 
// Attribute Inspector.
NSDictionary *externalObjects = [NSDictionary dictionaryWithObjectsAndKeys:
                                    targetObject, @"TargetObject",
                                    nil];

// Load the NIBs ready for instantiation.
UINib *portraitViewNib = [UINib nibWithNibName:@"Weekview_iPad_Portrait" bundle:nil];
UINib *landscapeViewNib = [UINib nibWithNibName:@"Weekview_iPad_Landscape" bundle:nil];

// Set up the NIB options pointing to your external objects dictionary.
NSDictionary *nibOptions = [NSDictionary dictionaryWithObjectsAndKeys:
                               externalObjects, UINibExternalObjects, 
                               nil];

// Instantiate the objects in the nib passing in the owner 
// (coincidentally also a proxy in the NIB) and your options dictionary.
NSArray *portraitViewArray =  [portraitViewNib instantiateWithOwner:owner
                                                            options:nibOptions];
NSArray *landscapeViewArray = [landscapeViewNib instantiateWithOwner:owner
                                                             options:nibOptions];

self.view = [(UIInterfaceOrientationIsPortrait(interfaceOrientation)) ? portraitViewArray : landscapeViewArray) objectAtIndex:0];

Примечания

Вы используете NSBundle для загрузки NIB.Вы должны использовать UINib.Это намного быстрее при чтении файлов NIB.Кроме того, он отделяет загрузку NIB от создания объекта.Это означает, что вы можете загрузить NIB в init, awakeFromNib или viewDidLoad, а не создавать экземпляр содержимого NIB.Затем в последний возможный момент, когда вам понадобятся реальные объекты в NIB, вы можете вызвать instantiateWithOwner.

Скажем, в случае использования UITableViewController вы бы загружали кончики вашей ячейки таблицы в viewDidLoad, но фактически не создавали их экземплярывам нужна ячейка этого типа в -tableView: cellForRowAtIndexPath:.

0 голосов
/ 05 августа 2011

На основе interfaceOrientation вы можете определить ориентацию и сбросить фреймы в элементах управления в формате xib. Более подробно это поможет вам ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...