iOS:滑动 UIView 开/关屏幕

发布于 2024-11-26 18:27:37 字数 6574 浏览 2 评论 0原文

我正在开发一个应用程序,在左侧有一个“抽屉”会非常有用。我正在做一些初步测试,看看如何最好地实现这一目标,但我遇到了一些非常基本的问题。

我的设置
1. 我在 Xcode 4 中使用单视图应用程序模板。
2. 在 xib 的“主/边框”视图中,我添加了 2 个 UIView(LeftPanel 和 RightPanel)和一个 UIButton(ShowHideButton)。
3. 我将 LeftPanel 涂成绿色,将 RightPanel 涂成蓝色,以便于查看。
4. 加载视图后,两个面板均可见,并且 UIButton 具有文本“隐藏面板”。
5. 按下按钮后,LeftPanel 应滑出屏幕(向左),而 RightPanel 应展开以占据其原始空间加上 LeftPanel 腾出的空间。
6. 此时,ShowHideButton 应将其文本更改为“Show Panel”。
7. 再次按下按钮后,LeftPanel 应滑回屏幕(从左侧),而 RightPanel 应缩小以“归还”其原始空间。
8. 此时,ShowHideButton 应将其文本更改回“隐藏面板”。

我正在使用animateWithDuration:animations:completion:实现动画。到目前为止,OFF 屏幕的转换工作正常(实际上非​​常好)。

令我困扰的是,当我尝试将 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

我错过了一些超级基本的东西吗?有人可以帮忙吗?

谢谢!

编辑: 我又尝试了两件事:
1.问题似乎与将离屏视图带入屏幕有关,因为从离屏的 LeftPanel 开始给我带来了同样的问题。
2. 单步执行代码确实会导致 Xcode(Lion 的 4 Beta)崩溃。以下是详细信息(每次崩溃都相同):

/SourceCache/DVTFoundation/DVTFoundation-867/Framework/Classes/FilePaths/DVTFilePath.m:373 中的断言失败 详细信息:空字符串不是有效路径 目的: 方法: +_filePathForParent:fileSystemRepresentation:length:allowCreation: 主题:{name = (null), num = 55} 提示:无 回溯: 0 0x00000001068719a6 -[IDEAssertionHandler handleFailureInMethod:object:fileName:lineNumber:messageFormat:arguments:](在 IDEKit 中) 1 0x0000000105f3e324 _DVTAssertionFailureHandler(在 DVTFoundation 中) 2 0x0000000105edd16f +[DVTFilePath _filePathForParent:fileSystemRepresentation:length:allowCreation:](在 DVTFoundation 中) 3 0x0000000105edcd4d +[DVTFilePath _filePathForParent:pathString:](在 DVTFoundation 中) 4 0x0000000105ede141 +[DVTFilePath filePathForPathString:](在 DVTFoundation 中) 5 0x00000001064a8dde -[IDEIndex queryProviderForFile:highPriority:](在 IDEFoundation 中) 6 0x000000010655193b -[IDEIndex(IDEIndexQueries)symbolsMatchingName:inContext:withCurrentFileContentDictionary:](在 IDEFoundation 中) 7 0x000000010aca6166 __68-[IDESourceCodeEditorsymbolsForExpression: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 中)

更新:屏幕截图
显示面板(启动状态) 显示面板
面板隐藏(按下按钮后成功转换) 面板隐藏
错误:再次按下按钮会导致失败 错误


I'm working on an app where having a "drawer" on the left-hand side would be very beneficial. I'm doing some initial testing to see how I would best accomplish this, and I'm having some very basic trouble.

My Setup
1. I am using a single-view application template in Xcode 4.
2. To the xib's "main/border" view, I've added 2 UIViews (LeftPanel and RightPanel) and a UIButton (ShowHideButton).
3. I've colored the LeftPanel green and the RightPanel blue for easier visibility.
4. When the view is loaded, both panels are visible and the UIButton has the text "Hide Panel".
5. Upon pressing the button, LeftPanel should slide off the screen (to the left) and RightPanel should expand to take up its original space plus the space vacated by LeftPanel.
6. At this point, ShowHideButton should change its text to "Show Panel".
7. Upon pressing the button again, LeftPanel should slide back onto the screen (from the left) and RightPanel should shrink to "give it back" its original space.
8. At this point, ShowHideButton should change its text back to "Hide Panel".

I'm implementing the animation using animateWithDuration:animations:completion:. So far, the transition OFF the screen is working fine (very well, actually).

What's troubling me is that when I then try to bring LeftPanel "back", I'm getting an EXC_BAD_ACCESS. I've posted my code below, and I've looked at it, but I really can't see what I'm accessing that's been released (or whatever is causing the 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

Am I missing something super-basic? Can anybody help?

Thanks!

Edit:
I've tried 2 more things:
1. The problem seems to be related to bringing the off-screen view on-screen, as starting with LeftPanel off-screen gives me the same problem.
2. Stepping through the code reliably causes Xcode (4 Beta for Lion) to crash. Here are the details (same for every crash):

ASSERTION FAILURE in /SourceCache/DVTFoundation/DVTFoundation-867/Framework/Classes/FilePaths/DVTFilePath.m:373
Details: empty string is not a valid path
Object:
Method: +_filePathForParent:fileSystemRepresentation:length:allowCreation:
Thread: {name = (null), num = 55}
Hints: None
Backtrace:
0 0x00000001068719a6 -[IDEAssertionHandler handleFailureInMethod:object:fileName:lineNumber:messageFormat:arguments:] (in IDEKit)
1 0x0000000105f3e324 _DVTAssertionFailureHandler (in DVTFoundation)
2 0x0000000105edd16f +[DVTFilePath _filePathForParent:fileSystemRepresentation:length:allowCreation:] (in DVTFoundation)
3 0x0000000105edcd4d +[DVTFilePath _filePathForParent:pathString:] (in DVTFoundation)
4 0x0000000105ede141 +[DVTFilePath filePathForPathString:] (in DVTFoundation)
5 0x00000001064a8dde -[IDEIndex queryProviderForFile:highPriority:] (in IDEFoundation)
6 0x000000010655193b -[IDEIndex(IDEIndexQueries) symbolsMatchingName:inContext:withCurrentFileContentDictionary:] (in IDEFoundation)
7 0x000000010aca6166 __68-[IDESourceCodeEditor symbolsForExpression:inQueue:completionBlock:]_block_invoke_01561 (in IDESourceEditor)
8 0x00007fff93fb490a _dispatch_call_block_and_release (in libdispatch.dylib)
9 0x00007fff93fb615a _dispatch_queue_drain (in libdispatch.dylib)
10 0x00007fff93fb5fb6 _dispatch_queue_invoke (in libdispatch.dylib)
11 0x00007fff93fb57b0 _dispatch_worker_thread2 (in libdispatch.dylib)
12 0x00007fff8bb5e3da _pthread_wqthread (in libsystem_c.dylib)
13 0x00007fff8bb5fb85 start_wqthread (in libsystem_c.dylib)

Update: Screen Shots
Panel Shown (startup state)
Panel Shown
Panel Hidden (successful transition after button press)
Panel Hidden
Error: Pressing button again causes failure
Error


如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

猫卆 2024-12-03 18:27:37

经过一番尝试并用头撞墙后,我终于得出结论:除非我误解了有关块的某些内容,否则我的代码没有错误。在调试中单步调试代码得到的答案与我预期的完全一样,但在动画块结束和完成块开始之间,不断弹出 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;
    }
}

After playing around with this a bunch and banging my head against the wall, I finally came to the conclusion that my code wasn't wrong unless I misunderstood something about blocks. Stepping through my code in debug yielded answers exactly like I expected, but between the end of the animation block and the beginning of the completion block, an EXC_BAD_ACCESS kept popping up.

Anyway, I'm not sure where I got the idea, but I figured I might want to try doing my mathematical calculations (for changing the frames) outside of my animation block.

Guess what? IT WORKED!

So, without further ado, here's the working code for what I wanted:

- (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;
    }
}
っ〆星空下的拥抱 2024-12-03 18:27:37

我认为您的崩溃与 UIKit 遇到的不确定状态问题有关,无论何时您将主线程置于负载状态或处于视图转换中间。当您在动画块中与块外检索数学计算的值时,它们是访问 UIView 模型的不同上下文,因此在这种情况下您在动画上下文中遇到内存错误并不感到惊讶。

I think your crash had to do with the indeterminate state problems you can get with UIKit whenever you put the main thread under load or are in the middle of a view transition. When you retrieve your values for your mathematical calculation your in the animation block vs outside the block, they are different contexts to access the UIView model, so I'm not surprised in the animation context you getting a memory error in that case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文