《iOS创意程序设计家》——第6.5节Undo与Redo机制

6.5 Undo与Redo机制
iOS创意程序设计家
还记得第5章在提到UITextView的时候是怎样实现Undo机制的吗?其实,iPhone OS3.0以后就内建了Undo-Redo的机制。在默认的情况下,每一个应用程序的window对象都提供一个NSUndoManager对象,用以管理Undo与Redo的操作,而窗口内的每一个控件也都有其各自的NSUndoManager对象。

这个Undo-Redo机制是怎么运作的呢?首先,它会有两组堆栈(stack),分别用来存放Undo与Redo的操作,而每一个操作都会以NSInvocation这个类来封装。很明显地,在这个类里面必须记录这个操作所使用的选择器(selector)、这个选择器的信息接收者以及所有的参数。

这里用一个例子来说明这一对堆栈的运作情况。假设有一个文本框textView,用户将原先的文字“ABC”修改为“DEF”,则这个修改的操作将会被以“反向”操作方式推入堆栈,也就是说将用户输入的“DEF”的文字还原为“ABC”的操作,如图6.20所示。

当我们按下“Undo”后,textView的文字会改回“ABC”,而这时候NSInvocation A会推出Undo堆栈,此时,Redo堆栈则是一个与NSInvocation A相反的操作NSInvocation B。

从上面的说明来看,当执行了一项操作时,我们必须要提供给NSUndoManager一个反向的操作。例如:当我们新增了一笔数据时,其反向操作就是将这笔数据删除。看起来,我们的程序代码似乎得多写不少东西。好在大部分的图形化控件都有其各自的NSUndoManager,所以要在原有的程序上加上Undo与Redo的功能其实是很简单的。接下来,我们可以把在第5章写的UITextViewDemo这个例子稍微改写一下,如下所示:

-(void) undoInput:(id) sender {
  self.navigationItem.leftBarButtonItem = nil;
  [[content undoManager] undo];
  // [content setText:prevText];
}

上面程序中批注的部分就是我们尚未介绍NSUndoManager之前的做法,但是有了NSUndoManager之后,只需要简简单单的一行就可以实现Undo的功能了。当然,也可以再加上Redo的功能,只要调用[[content undoManager] redo]就可以了。

虽然部分图形化控件都提供这种功能,但是,我们是否也可以将Undo-Redo机制应用在其他非图形化控件上呢?其实也是可以的,不过我们需要自己产生一个NSUndoManager,并建立相对应的Undo与Redo机制。例如:

NSUndoManager *manager = [NSUndoManager new];
还可以通过 prepareWithInvocationTarget:来建立您的Undo的操作。

[[manager prepareWithInvocationTarget:self] myUndoAction];
有了Invocation之后就可以调用Undo与Redo方法来进行Undo与Redo。

应用范例:破裂的手机
现在我们要应用Undo-Redo机制以及摇晃事件的检测来制作一个有趣的应用程序。这个应用程序一开始会让界面呈现破碎的样子,当然这并非是真的,而是我们制作出来的效果。但是当其他人看到这个界面的时候,肯定会很吃惊。这时候,只要摇晃一下手机就会恢复原状,再摇晃一次界面就会变成破碎的模样。

学习重点:

Undo-Redo机制的使用
摇晃事件的应用
动画的使用
建立一个Single View的项目,并命名为“BrokeniPhone”。

在建立项目过程中,请记得勾选“Use Storyboard”以及“Use Automatic Reference Counting”选项。

在项目中加入两张手机的界面。

在设计界面中加入Image View控件,如图6.21所示。

打开“MainStorybard.storyboard”并从控件库中加入一个Image View控件到界面中。这时候,请指定该控件的图形为破碎后的手机界面。

选中界面中的手机背景,然后打开编辑器的辅助模式,按住“Control”键后拉到ViewController.h以建立Outlet。完成后,我们先在ViewController.h中定义一个NSUndoManager的变量以及restoreScreen和breakScreen这两个方法,代码如下:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController{
@private
  NSUndoManager *undoManager;
 }
@property (strong, nonatomic) IBOutlet UIImageView *screen;
-(void) restoreScreen;
-(void) breakScreen;
@end

打开ViewController.m,并编辑如下:

处理晃动事件的第一步就是让目前的ViewController可以成为First Responder。

// 让ViewController可以成为First Responder
- (BOOL) canBecomeFirstResponder {
  return YES;
}
接着要让ViewController变成First Responder,而在界面消失后让ViewController变成非First Responder。

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  // 让ViewController变成First Responder  
  [self becomeFirstResponder];
}

- (void)viewDidDisappear:(BOOL)animated {
   [super viewDidDisappear:animated];
  // 让ViewController变成非First Responder  
  [self resignFirstResponder];
}
最后,加入摇晃操作的检测事件处理就好了。

- (void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
  if (motion==UIEventSubtypeMotionShake) {
    if ([undoManager canUndo]) {
       [undoManager undo];
    }else{
       [undoManager redo];
    }
  }
}

由于我们要让undoManager在Undo与Redo间不断切换,因此,我们可以通过canUndo这个方法来判断在Undo堆栈里面是否有Invocation。

这样就完成了晃动事件的处理。

现在开始初始化undoManager。由于刚开始的界面是破碎的模样,因此,我们在Undo的堆栈里面要建立一个恢复原状的Invocation。

- (void)viewDidLoad {
  [super viewDidLoad];

  undoManager = [NSUndoManager new];
  [[undoManager prepareWithInvocationTarget:self] restoreScreen];
}
建立好Invocation后,undoManager就可以进行Undo了。

7.jpg 建立界面破碎以及恢复界面的操作。

- (void) breakScreen {
  [UIView transitionWithView:screen
     duration:0.5f options:UIViewAnimationOptionTransitionCrossDissolve animations: ^(void){
     screen.image = [UIImage imageNamed:@"broken.png"];
  } completion:nil];
  [[undoManager prepareWithInvocationTarget:self] restoreScreen];  
}

- (void) restoreScreen {
  [UIView transitionWithView:screen
     duration:0.5f options:UIViewAnimationOptionTransitionCrossDissolve animations:^(void){
     screen.image = [UIImage imageNamed:@"normal.png"];
  } completion:nil];
  [[undoManager prepareWithInvocationTarget:self] breakScreen];
}

这两个方法的操作刚好相反,包括加载的图形以及堆栈内的Invocation。我们在每一个操作内都要去建立相反的操作,这样在堆栈内才有相反的操作可以还原回去。现在,您可以拿着这个应用程序去吓唬您的朋友,相信他们应该会吓一跳吧。不过这样的应用程序并不适合上架,被Apple拒绝的几率也十分高,读者们可以好好思考一下其中的原因。

时间: 2024-10-21 17:24:29

《iOS创意程序设计家》——第6.5节Undo与Redo机制的相关文章

《iOS创意程序设计家》——第6.4节事件检测

6.4 事件检测 iOS创意程序设计家 界面控制器除了负责界面的管理以及布局外,还负责事件的传递.这些事件包括我们在第5章已经介绍过的触控事件,还有接下来要介绍的晃动检测事件.这些事件都定义在UIResponder类里面,而无论是界面控制器UIViewController还是界面UIView,它们都继承自UIResponder类. 6.4.1 晃动检测 首先,我们来看看晃动事件的处理.与触控事件类似的是,晃动检测也是由一连串的事件所组成的,不过,要让您的应用程序支持晃动检测,必须让您的界面控制器

《iOS创意程序设计家》——第6.2节导航栏控制器UINavigationController

6.2 导航栏控制器UINavigationController iOS创意程序设计家 导航栏控制器(UINavigationController)位于界面的最上方,主要用于将具有因果关系的界面连接起来,它由几个元素组成:左边按钮.右边按钮以及标题.我们可以通过导航栏的navigationItem来访问这3个元素.其中,左右两边的按钮都是UIBarButtonItem类,我们可以通过navigationItem.rightBarButtonItem以及navigationItem.leftBar

《iOS创意程序设计家》——第6.1节 界面的管理

6.1 界面的管理iOS创意程序设计家UIViewController提供了对于iOS应用程序的基本界面管理模式,包括界面的呈现.Modal窗口界面的管理以及iOS设备旋转后界面旋转的支持等. 每个UIViewController都会对应到一个唯一的UIView,这表示这个界面由相对应的UIViewCon-troller所控制.如同我们在前一章所提到的,在UIViewController类中,有一个view的属性,而UIViewController正是通过这个属性才得以与其管理的界面关联起来.在

《iOS创意程序设计家》——第6.3节标签页控制器UITabBarController

6.3 标签页控制器UITabBarControlleriOS创意程序设计家相对于导航栏控制器,标签页控制器(UITabBarController)是将几个具有独立功能的界面使用Tab分隔开来,并定义在界面的下方.这些界面会存放在一个数组里面,而里面每个元素则是UIView-Controller的类,当然也可以包括导航栏UINavigationController在内.可以通过viewControllers属性或是setViewControllers: animated:方法来设置标签页控制器所

《iOS创意程序设计家》——导读

目 录第1章 从硬件的认识开始第2章 Objective-C语言的基础第3章 开发工具第4章 基本类与机制第5章 界面控件第6章 界面控制器 6.1 界面的管理 6.2 导航栏控制器UINavigationController 6.3 标签页控制器UITabBarController 6.4 事件检测 6.5 Undo与Redo机制 6.6 热身操第7章 操作信息的截取第8章 数据访问与打印第9章 表格控件与Core Data第10章 通讯录第11章 相机与相簿第12章 影音多媒体第13章 定位

【IOS开发必收藏】详解IOS应用程序内使用IAP/STOREKIT付费、沙盒(SANDBOX)测试、创建测试账号流程!【2012-12-11日更新获取”产品付费数量等于0的问题”】

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2d/550.html //--2012-12-11日更新   获取"产品付费数量等于0这个问题"的原因 看到很多童鞋问到,为什么每次都返回数量等于0?? 其实有童鞋已经找到原因了,原因是你在 ItunesConnect 里的 "Contracts, Tax, and Banking "没

IDA反汇编/反编译静态分析iOS模拟器程序(八)IDA for Mac

iOS多用OC(Objective-C)编程,Mac也类似,所以IDA for Mac对OC的支持似乎强些.Windows的IDA在反汇编某些SDK库文件时会识别不出OC的函数名,而且对OC运行时的结构体也没识别出来.当然,因为我用的是6.1版的windows IDA, Mac上用的是6.4版,不知道是不是windows IDA 6.1的bug了. 总之在界面操作流程上,感觉Mac IDA是对OC有做优化的.当加载一个app时, 会询问是否解析和重命名OC的函数: 如果选择No,所有OC函数都会

IDA反汇编/反编译静态分析iOS模拟器程序(五)F5反编译

反编译是IDA最让人振奋的功能,它的本质是IDA的一个插件,不过会被当做hex-rays的另一个产品.既然是产品,那当然就另外收费,demo版是没有的.反编译的快捷键是F5,菜单位置在 顶部菜单View->Open Subviews->Pseudocode.(有网友问到为什么按照第一篇的地址下载IDA后也没有F5,最终是发现他自己装了python,设了环境变量,这会令IDA工作不正常) 在显示反汇编的窗口中按F5,经过分析后,会多了一个标签栏Pseudocode-A: 继续上一节(可用两个浏览

ios闹钟程序-iOS闹钟程序实现的过程

问题描述 iOS闹钟程序实现的过程 求iOS闹钟程序实现的过程,不要是早几年的,新版本的,感激不尽,非常感谢 解决方案 http://download.csdn.net/detail/u012524508/6431991