iOS: скольжение UIView вкл / выкл экран
Я работаю над приложением, в котором наличие "ящика" на левой стороне было бы очень полезно. Я делаю некоторые начальные тесты, чтобы увидеть, как мне лучше всего это сделать, и у меня возникли некоторые базовые проблемы.
Моя настройка
1. Я использую шаблон приложения с одним представлением в Xcode 4.
2. К представлению xib "main / border" я добавил 2 UIViews (LeftPanel и RightPanel) и UIButton (ShowHideButton).
3. Я покрасил зеленый цвет левой панели и синий цвет правой панели для лучшей видимости.
4. Когда представление загружено, обе панели видны, а на кнопке UIB есть текст "Скрыть панель".
5. После нажатия кнопки LeftPanel должна соскользнуть с экрана (влево), а RightPanel должна развернуться, чтобы занять свое первоначальное пространство плюс пространство, оставленное LeftPanel.
6. На этом этапе ShowHideButton должен изменить свой текст на "Показать панель".
7. При повторном нажатии кнопки LeftPanel должна снова сместиться на экран (слева), а RightPanel должна сжаться, чтобы "вернуть" свое первоначальное пространство.
8. На этом этапе ShowHideButton должен изменить свой текст обратно на "Скрыть панель".
Я реализую анимацию, используя animateWithDuration:animations:completion:
, Пока что переход с выключенного экрана работает нормально (очень хорошо, на самом деле).
Меня беспокоит то, что когда я пытаюсь вернуть LeftPanel, я получаю EXC_BAD_ACCESS. Я разместил свой код ниже и посмотрел его, но я действительно не вижу того, к чему я обращаюсь, что было выпущено (или что вызывает EXC_BAD_ACCESS).
DrawerTestingViewController.h
#import <UIKit/UIKit.h>
typedef enum {
kHidden,
kShown
} PanelState;
@interface DrawerTestingViewController : UIViewController {
PanelState currentState;
UIButton *showHideButton;
UIView *leftPanel;
UIView *rightPanel;
}
@property (assign, nonatomic) PanelState CurrentState;
@property (strong, nonatomic) IBOutlet UIButton *ShowHideButton;
@property (strong, nonatomic) IBOutlet UIView *LeftPanel;
@property (strong, nonatomic) IBOutlet UIView *RightPanel;
- (IBAction)showHidePressed:(id)sender;
@end
DrawerTestingViewController.m
#import "DrawerTestingViewController.h"
@implementation DrawerTestingViewController
@synthesize CurrentState = currentState;
@synthesize LeftPanel = leftPanel;
@synthesize RightPanel = rightPanel;
@synthesize ShowHideButton = showHideButton;
#pragma mark - My Methods
- (IBAction)showHidePressed:(id)sender
{
switch ([self CurrentState]) {
case kShown:
// Hide the panel and change the button's text
// 1. Hide the panel
[UIView animateWithDuration:0.5
animations:^{
// b. Move left panel from (0, 0, w, h) to (-w, 0, w, h)
CGRect currLeftPanelRect = [[self LeftPanel] frame];
currLeftPanelRect.origin.x = -1 * currLeftPanelRect.size.width;
[[self LeftPanel] setFrame:currLeftPanelRect];
// c. Expand right panel from (x, 0, w, h) to (0, 0, w + x, h)
CGRect currRightPanelRect = [[self RightPanel] frame];
currRightPanelRect.origin.x = 0;
currRightPanelRect.size.width += currLeftPanelRect.size.width;
[[self RightPanel] setFrame:currRightPanelRect];}
completion:NULL];
// 2. Change the button's text
[[self ShowHideButton] setTitle:@"Show Panel" forState:UIControlStateNormal];
// 3. Flip [self CurrentState]
[self setCurrentState:kHidden];
break;
case kHidden:
// Show the panel and change the button's text
// 1. Show the panel
[UIView animateWithDuration:0.5
animations:^{
// b. Move left panel from (-w, 0, w, h) to (0, 0, w, h)
CGRect currLeftPanelRect = [[self LeftPanel] frame];
currLeftPanelRect.origin.x = 0;
[[self LeftPanel] setFrame:currLeftPanelRect];
// c. Expand right panel from (0, 0, w, h) to (leftWidth, 0, w - leftWidth, h)
CGRect currRightPanelRect = [[self RightPanel] frame];
currRightPanelRect.origin.x = currLeftPanelRect.size.width;
currRightPanelRect.size.width -= currLeftPanelRect.size.width;
[[self RightPanel] setFrame:currRightPanelRect];}
completion:NULL];
// 2. Change the button's text
[[self ShowHideButton] setTitle:@"Hide Panel" forState:UIControlStateNormal];
// 3. Flip [self CurrentState]
[self setCurrentState:kShown];
break;
default:
break;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setCurrentState:kShown];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
switch ([self CurrentState]) {
case kShown:
[[self ShowHideButton] setTitle:@"Hide Panel" forState:UIControlStateNormal];
break;
case kHidden:
[[self ShowHideButton] setTitle:@"Show Panel" forState:UIControlStateNormal];
break;
default:
break;
}
}
@end
Я что-то упускаю сверхосновное? Кто-нибудь может помочь?
Спасибо!
Изменить: я пробовал еще 2 вещи:
1. Проблема, по-видимому, связана с выводом на экран вне экрана, так как запуск с левой стороны вне экрана дает мне ту же проблему.
2. Надежное выполнение кода приводит к сбою Xcode (4 Beta for Lion). Вот детали (то же самое для каждого сбоя):
СБОЙ В СБОРЕ в /SourceCache/DVTFoundation/DVTFoundation-867/Framework/Classes/FilePaths/DVTFilePath.m:373 Подробности: пустая строка не является допустимым путем Объект: Метод: +_filePathForParent:fileSystemRepresentation:length:allowCreation: Thread {name: (null), num = 55} Подсказки: нет Обратный след: 0 0x00000001068719a6 -[IDEAssertionHandler handleFailureInMethod:object:fileName:lineNumber:messageFormat:arguments:] (в IDEKit) 1 0x0000000105f3e324 _FileDefile_Play_File_File_Play_File_Play_File_Rail_File_Rail_Play_File_Rail_File_Rail_Play_File_Rile: length: allowCreation:] (в DVTFoundation) 3 0x0000000105edcd4d +[DVTFilePath _filePathForParent: pathString:] (в DVTFoundation) 4 0x0000000105ede141 +[файл DVTFilePathPathForPathString: указатель_строка: 0: IDFDF0_0_0_0_0_0_0_0_0_0_0_2_0_0_0_0_0_0_0_2_0_0_0_0_0_0_2_2_2_0_0_0_0_0_0Дидр.др.Дж.в.ДЗ_ДВИЖУЩАЯ_ДНЕЖЕМАЯ ИНФОРМАЦИЯ ПО ОБЪЕДИНЕНИЮ В ОБЛАСТИ ДВИЖЕНИЯ: ID: 6 0x000000010655193b -[IDEIndex(IDEIndexQueries) symbolsMatchingName:inContext:withCurrentFileContentDictionary:] (в IDEFoundation) 7 0x000000010aca6166 __68-[IDESourceCodeEditor symbolsForExpression:inQueue:completionBlock:]_block_invoke_01561 (в IDESourceEditor) 8 0x00007fff93fb490a _dispatch_call_block_and_release (в libdispatch.dylib) 9 0x00007fff93fb615a _dispatch_queue_drain (в libdispatch.dylib) 10 0x00007fff93fb5fb6 _dispatch_queue_invoke (в libdispatch.dylib) 11 0x00007fff93fb57b0 _dispatch_worker_thread2 (в libdispatch.dylib) 12 0x00007fff8bb5e3da _pthread_wqthread (в libsystem_c.dylib) 13 0x00007fff8bb5fb85 start_wqthread (в libsystem_c.dylib)
Обновление: скриншоты
Панель Показана (состояние запуска)
Панель скрыта (успешный переход после нажатия кнопки)
Ошибка: повторное нажатие кнопки вызывает сбой
2 ответа
Поиграв с этой кучей и ударившись головой о стену, я, наконец, пришел к выводу, что мой код не ошибся, если я что-то не понял в блоках. Пройдя по моему коду в отладке, я получил ответы в точности так, как я ожидал, но между концом блока анимации и началом блока завершения продолжал появляться EXC_BAD_ACCESS.
В любом случае, я не уверен, откуда у меня появилась идея, но я подумал, что, возможно, захочу попытаться выполнить свои математические вычисления (для изменения кадров) вне моего блока анимации.
Угадай, что? ЭТО СРАБОТАЛО!
Итак, без лишних слов, вот рабочий код для того, что я хотел:
- (IBAction)showHidePressed:(id)sender
{
switch ([self CurrentState]) {
case kShown:
{
// Hide the panel and change the button's text
CGRect currLeftPanelRect = [[self LeftPanel] frame];
currLeftPanelRect.origin.x -= currLeftPanelRect.size.width / 2;
CGRect currRightPanelRect = [[self RightPanel] frame];
currRightPanelRect.origin.x = 0;
currRightPanelRect.size.width += currLeftPanelRect.size.width;
// 1. Hide the panel
[UIView animateWithDuration:0.5
animations:^{
// b. Move left panel from (0, 0, w, h) to (-w, 0, w, h)
[[self LeftPanel] setFrame:currLeftPanelRect];
// c. Expand right panel from (x, 0, w, h) to (0, 0, w + x, h)
[[self RightPanel] setFrame:currRightPanelRect];
}
completion:^(BOOL finished){ if(finished) {
[[self ShowHideButton] setTitle:@"Show Panel" forState:UIControlStateNormal];
[self setCurrentState:kHidden];
}
}];
}
break;
case kHidden:
{
// Show the panel and change the button's text
// 1. Show the panel
[UIView animateWithDuration:0.5
animations:^{
// b. Move left panel from (-w, 0, w, h) to (0, 0, w, h)
CGRect currLeftPanelRect = [[self LeftPanel] frame];
currLeftPanelRect.origin.x += currLeftPanelRect.size.width / 2;
[[self LeftPanel] setFrame:currLeftPanelRect];
// c. Expand right panel from (0, 0, w, h) to (leftWidth, 0, w - leftWidth, h)
CGRect currRightPanelRect = [[self RightPanel] frame];
currRightPanelRect.origin.x = currLeftPanelRect.size.width;
currRightPanelRect.size.width -= currLeftPanelRect.size.width;
[[self RightPanel] setFrame:currRightPanelRect];
}
completion:^(BOOL finished){ if(finished) {
[[self ShowHideButton] setTitle:@"Hide Panel" forState:UIControlStateNormal];
[self setCurrentState:kShown];
}
}];
}
break;
default:
break;
}
}
Я думаю, что ваш сбой был связан с неопределенными проблемами состояния, которые вы можете получить с помощью UIKit всякий раз, когда вы загружаете основной поток или находитесь в середине перехода представления. Когда вы извлекаете свои значения для своих математических вычислений в блоке анимации и вне блока, это разные контексты для доступа к модели UIView, поэтому я не удивлен, что в контексте анимации вы получаете ошибку памяти в этом случае.