在Swift中实现撤销功能-阿里云开发者社区

开发者社区> 玄学酱> 正文

在Swift中实现撤销功能

简介: 本文讲的是在Swift中实现撤销功能,在过去的一段时间里,有很多的Blog推出了关于他们想在Swift中所添加的动态特性的文章。事实上Swift 已经成为了一门具有相当多动态特性的语言:它拥有泛型,协议, 头等函数(译者注1:first-class function指函数可以向类一样作为参数传递)
+关注继续查看
本文讲的是在Swift中实现撤销功能,


在过去的一段时间里,有很多的Blog推出了关于他们想在Swift中所添加的动态特性的文章。事实上Swift 已经成为了一门具有相当多动态特性的语言:它拥有泛型,协议, 头等函数(译者注1:first-class function指函数可以向类一样作为参数传递),和包含很多可以的动态操作的函数的标准库,比如mapfilter等(这意味着我们可以利用更安全更灵活的函数来代替 KVC 来使用 字符串)(译者注2:KVC指Key-Value-Coding一个非正式的 Protocol,提供一种机制来间接访问对象的属性)。对于大多数人而言,特别希望介绍反射这一特性,这意味着他们可以在程序运行时进行观察和修改。

Swift中,反射机制受到很多的限制,但是你仍然你可以在代码运行的时候动态的生成和插入一些东西。 比如这里是怎样为NSCoding或者是JSON动态生成字典的实例。

今天在这里,我们将一起看一下在Swift中怎样去实现撤销功能。 其中一种方法是通过利用Objective-C中基于的反射机制所提供的NSUndoManager。通过利用struct,我们可以利用不同的方式在我们的APP中实现撤销这一功能。 在教程开始之前,请务必确保你自己已经理解了Swiftstruct的工作机制(最重要的是理解他们都是独立的拷贝)。
首先要声明的一点是,这篇文章并不是想告诉大家我们不需要对runtime进行操作,或者我们提供的是一种NSUndoManager的替代品。这篇文章只是告诉了大家一种不同的思考方式而已。

我们首先创建一个叫做UndoHistorystruct。 通常而言,创建UndoHistory时会伴随一个警告,提示只有当A是一个struct的时才会生效。为了保存所有状态信息,我们需要将其存放入一个数组之中。当我们修改了什么时,我们只需要将其push进数组中,当我们希望进行撤回时,我们将其从数组中pop出去。我们通常希望有一个初试状态,所以我们需要建立一个初始化方法:

    struct UndoHistory<A> {
        private let initialValue: A
        private var history: [A] = []
        init(initialValue: A) {
            self.initialValue = initialValue
        }
    }

举个例子,如果我们想在一个tableViewController中通过数组的方式提供撤销操作,我们可以创建这样一个struct

    var history = UndoHistory(initialValue: [1, 2, 3])

对于不同情境下的撤销操作,我们可以创建不同的struct来实现:

    struct Person {
        var name: String
        var age: Int
    }
    var personHistory = UndoHistory(initialValue: Person(name: "Chris", age: 31))

当然,我们希望获得当前的状态,同时设置当前状态。(换句话说:我们希望实时地操作我们的历史记录)。我们可以从history数组中的最后一项值来获取我们的状态,同时如果数组为空的话,我们便返回我们的初始值。 我们可以通过将当前状态添加至history数组来改变我们的操作状态。

    extension UndoHistory {
        var currentItem: A {
            get {
                return history.last ?? initialValue
            }
            set {
                history.append(newValue)
            }
        }
    }

比如,如果我们想修改个人年龄(译者注3:指前面作者编写的Person结构体中的age属性), 我们可以通过重新计算属性来很轻松的做到这一点:

    personHistory.currentItem.age += 1
    personHistory.currentItem.age // Prints 32

当然,undo 方法的编写并未完成。对于从数组中移出最后一个元素来讲是非常简单的。 根据你自己的声明,你可以在数组为空的时候抛出一个异常,不过,我没有选择这样一种做法。

    extension UndoHistory {
        mutating func undo() {
            guard !history.isEmpty else { return }
            history.removeLast()
        }
    }

很简单的使用它(译者注4:这里指作者前面所编写的undo相关代码)

    personHistory.undo()
    personHistory.currentItem.age // Prints 31 again
~

当然,我们到现在的UndoHistory操作只是基于一个很简单的Person类。比如,如果我们想利用Array来实现一个tableviewcontrollerundo操作,我们可以利用属性来获取从数组中得到的元素:

    final class MyTableViewController<item>: UITableViewController {
        var data: UndoHistory<[item]>

        init(value: [Item]) {
            data = UndoHistory(initialValue: value)
            super.init(style: .Plain)
        }

        override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return data.currentItem.count
        }

        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCellWithIdentifier("Identifier", forIndexPath: indexPath)
            let item = data.currentItem[indexPath.row]
            // configure `cell` with `item`
            return cell
        }

        override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
            guard editingStyle == .Delete else { return }
            data.currentItem.removeAtIndex(indexPath.row)
        }
    }

struct中另一个非常爽的特性是:我们可以自由的使用监听者模式。 比如,我们可以修改data的值:

    var data: UndoHistory<[item]> {
        didSet {
            tableView.reloadData()
        }
    }

我们即使是修改数组内很深的值(比如:data.currentItem[17].name = "John"),我们通过didSet也能很方便地定位到修改的地方。当然,我们可能希望做一些例如reloadData这样方便的事情。比如, 我们可以利用Changeset 库来计算变化,然后来根据插入/删除/移动/等不同的操作来添加动画。

很明显的是, 这种方法有着它自身的缺点。例如,它保存了整个状态的历史操作,不是每次状态变化之间的不同点。 这种方法只使用了struct来实现undo操作 (更为准确的讲:是只使用了struct中值的一些特性)。这意味着,你并不需要去阅读 runtime编程指导这本书, 你只需要对structgenerics(译者注5:generics指泛型)有足够的了解。

  1. 为data.currentItem提供了一个可计算的属性 items 来进行获取和设置操作,是一个不错的想法。这使得data-sourcedelegate等方法的实现变得更为容易。
  2. 如果你想更进一步优化,这里有一些非常有意思的想法:添加恢复功能,或者是编辑功能。你可以在tableView中去实现, 如果你真的很天真的按照这个去做了,那么你会发现在你的undo历史中会存在重复记录。




作者:Zheaoli
链接:http://www.jianshu.com/p/ba6af299aeec
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。





原文发布时间为:2016年06月15日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
单元测试(UT)、功能测试(FT)(转)
纯个人总结: 单元测试(UT)、功能测试(FT): 目的:1、尽量避免写的代码测试人员频繁的来找你其他地方又出问题了;2、提供的接口不可用;3、一个bug修复了引入了其他的bug或者其他用例变红了; 理解:在实现函数功能的时候编写对应的测试代码,尽量保证”输入-输出”的正确性,在测试用例比较多...
1237 0
Day10 - JS 实现 Checkbox 中按住 Shift 的多选功能
Day10 - JS 实现 Checkbox 中按住 Shift 的多选功能 作者:©liyuechun简介:JavaScript30 是 Wes Bos 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。
1739 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4512 0
Redshift 新增12项功能
Amazon Redshift 是一种快速、完全托管的 PB 级数据仓库服务,它使得用现有商业智能工具对您的所有数据进行高效分析变得简单而实惠。您可以以每小时 0.25 USD 的价格从小规格做起,无需订立长期合约或预付费,然后以每年每 TB 1 000 USD 的价格再扩展到 1 PB 或以上,这
4513 0
Thrift结构分析及增加取客户端IP功能实现
Thrift结构分析及增加取客户端IP功能实现.pdf 目录 目录 1 1. 前言 1 2. 示例Service 1 3.
1363 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
5731 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
10776 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
3861 0
+关注
玄学酱
这个时候,玄酱是不是应该说点什么...
17436
文章
438
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载