swift int转string_用Swift开发macOS程序,九、目录模块

5a908347b5b6673cbb0c35c3b94d5aac.png
程序中跟目录模块相似的,也是通过使用Outline View组件显示内容的模块有:备注、搜索、角色、符号、字典等其它五个。设计完成本模块后将不再对其它模块进行说明,一切请查看Github上代码。目录模块的主要功能有:显示目录、目录项的增删改及排序,当前目录只支持章节两级结构。其主要代码将放在ViewController.swift中,我们需要留意的是其事件的处理与操作的流程。在用Swift开发macOS程序, 四、界面设计中,我们是实现过目录组件,希望你没有忘记,今天我们将拿过来用用。

进行到这里,我们将需要处理较复杂的业务逻辑。但要放心是,想信我们,一切都会不是问题。处理这类问题,首先要了解我们的业务,其次需要了我们用来处理业务的对象,如Outline View。像打仗一样,要做到“知己知彼“,方能“百战不殆”。

〜〜〜〜〜 概述 〜〜〜〜〜

从前面的布局设计中可以看出,我们是通过使用Outline View组件来实现目录模块业务的。所以在这里需要我们较详细地去了解Outline View组件,我们需要实现的业务有:显示目录、增加目录项、删除目录项、选择并修改目录项、拖移目录项进行排序等。

跟目录相关的,我们还需要实现各个章节的打开与关闭,章节在标题标签栏的呈现与排序,章节在内容区的呈现。

完成后效果如下:

e3fd4b14c524c9566a3ad7b940052bdb.png
https://www.zhihu.com/video/1147517397366935552

〜〜〜〜〜 正文 〜〜〜〜〜

一、 查看组所需要实现的协议方法。

在Xcode中,应用一个较复杂的组件,一般需要实现它的delegate、dataSource两类委托。一般代码文档里有说明告诉我们需要实现哪些委托方法,我们也可以通过Xcode输出错误信息的方式来获取需要实现委托方法的信息,下面以Outline View来作说明:

首先,需要通过Main.storyboard,建立目录Outline View组件在ViewController.swift文件中的Outlet,命名为“catalogOutlineView”;

@IBOutlet 

其次,确定在ViewController.swift的viewDidLoad()中实现其delegate、dataSource两类委托;

catalogOutlineView

再次,建立拓展类,确定引用该两类协议;

extension 

最后,我们Build and Run,我们将在Debug Area看到下面的信息,说明使用数据源需要实现4个委托方法。

Illegal NSOutlineView data source
 (<test.ViewController: 0x600002c05d80>). 
Must implement 
outlineView:numberOfChildrenOfItem:, 
outlineView:isItemExpandable:, 
outlineView:child:ofItem: 
and outlineView:objectValueForTableColumn:byItem:

二、了解Outline View。

现在,我们建立一个临时工程"Temp"来看看,该如何实现NSOutlineView的数据呈现与增、删、改操作。这是我个人学习时常用的方式,一遇到不熟悉的组件就会新建一个独立的工程,然后慢慢的试着应用。
  • 添加Outline View。
  1. 打开Main.storyboard,在最下的ViewController添加Outline View;
  2. 拖动上下左右边框到出边缘参考线,这时上下左右边距相等;
  3. 在Size栏,点击上下左右宽高约束,图里右侧的示意为:组件边距不变随窗体变化大小;
  4. 选择Document Outline中的Outline View项;
  5. 设置Attributes栏中,Content Mode项为Cell Based,Columns项为1,去掉勾选Headers;
  6. 选择Document Outline中的Table Column项,设置Identify栏中,identifier项为“title”。

2e1715b22c5cf0a286c359364f7085fa.png

81cfeb9d5571c873c3f41036f295af64.png

97884263d3c894e54fe66b28a3a7d8d5.png
  • 应用Outline View之Cell Based模式。
  1. 选择Document Outline中的Outline View项,建立ViewController.swift的Outlet为outlineView;
  2. 在viewDidLoad方法中添加代码:outlineView.dataSource = self;
  3. 添ViewController的类拓展,用来实现NSOutlineViewDataSource;
  4. Build and Run,显示Temp程序窗口;
  5. 输出错误提示。

dbf069402368b8a6112e0082688e1053.png

6. 我们按提示使用数组实现其4个协议方法:

import 

7. Build And Run,双击各项均可编辑,但无法保存编辑结果。

bff08ccd317ba5786ea7356ef4119e8f.png

8. 得到如上结构是不能满足我们的需要,因为我们需要在各项前添加图标。

  • 应用Outline View之View Based模式。
  1. 选择Document Outline,设置Attributes栏中,Content Mode项为View Based;再选择Table Cell View项;
  2. 设置Identify栏中,identifier项为“tableCellView”。

32bd3a1e367c8fafc3042aa5bc9d180a.png

3. 在outlineView.delegate =self下添加;

outlineView

4. 添ViewController的类拓展,用来实现NSOutlineViewDelegate,并实现第一个协议方法。注意,需要在代码里明确指出表格里的文字域为可编辑;

extension 

5. Build And Run,两次单击各项可编辑。

  • 为OutlineView添加Icon。
  1. 添加3个Image Assets,作为目录一二三级的图标,分别为“catalogLevel0”、“catalogLevel1”、“catalogLevel2”;
  2. 移除Document Outline栏的Table Cell View项;
  3. 从library中添加Image & Text Table Cell View组件到Document Outline栏的title项下,即替换原Table Cell View项,改其Identify栏中Identifier值为“titleCellView”;
  4. 在上述委托方法里添加下列代码,其中Image Literal会出现下拉窗口,让你选择Image Assets。
tableCellView

5. Build And Run,如下图:

118d80f51ac5de724830e1413a91b07a.png
  • 其它功能的实现。
到了这里已经实现了呈现的样式,但还有几个事情需要做到:第一,单击要打开对应的章节;第二,两次单击后进入编辑状态时需要全选文本;第三,完成修改需要保存;第四,上下移动章节项;第五,需要支持右键菜单。
  1. 单击要打开对应的章节,需要一个相关事件,这个我找到了。
/// 点击各行
    

2. 两次单击后进入编辑状态时,同时全选文本。这个我找了两天,用各种方法都不行,只好自定义一个NSTextField叫JYHTextField,并设置Table View Cell的Identity的Class项的值为“JYHTextField”。

/**

3. 完成修改需要保存。这一个需要NSTextField的委托NSTextFieldDelegate。

/// 完成编辑
    

4. 上下移动章节项,即Outline View实现拖动。首先,需要实现NSOutlineViewDataSource里的三个协议方法,下面有说明;然后,通过拖动与放置的位置,使用NSOutlineView的moveItem方法就可以实现。需要说明的是,我在实际应用时是修改原数据,通过更新OutlineView来实现的。

在这之前: 首先,需要在ViewController.swift文件的viewDidLoad方法里,注册拖移类型catalogOutlineView.registerForDraggedTypes([NSPasteboard.PasteboardType.string]); 然后,需要定义属性,var at = -1。
/// 接收方,拖移放置的位置。
    

Build and Run,拖动效果,三合一PS后如下:

e60223eb553e131a839062b008ff252e.png

5. 需要支持右键菜单。

首先,在Main.storyboard中为View Controller Scene添加一个Menu组件;
其次,设计好Menu组件的菜单项;
再次,为ViewController.swift添加Menu组件的Outlet为catalogMenu;
最后,为ViewController.swift添加catalogMenu各菜单项的Action。

8a03ab3dbd8e1d874fb194111436a534.png
  • 使用NSTreeController实现NSOutlineView。
使用NSTreeController实现NSOutlineView是一种较方便及较常用的方式。本案未使用该方式,只是看到网上相应的教程特别少或旧,所以讲一下。熟悉了这个,那么使用NSArrayController实现NSTableView你也会,因为方式方法都一样。好,一切还是从新建一个“Temp”工程开始。
  1. 在ViewController.swift上添加一个Chapter类,并在ViewController类上添加数组如下,代码部分只有这么多;
import 

2. 在Storyboard中的ViewController里添加一个Outline View组件及三个Push Button。Outline View设置为单列,无头,ViewBased。三个Push Button分别改为Title为“Add”、“AddChildren”、“Delete”,自已调整布局备用。再添加一个Tree Controller到ViewController,结果显示如下:

c20d6a9882d82526e3ec2b45c6ba5863.png

3. 选择Tree Controller的Attributes栏,设置Children项的值为“sub”,Leaf项的值为“end”,Mode为“Class”,ClassName为“Temple.Chapter”,注意“Temple.”不能少。意思是Tree Controller对应的节点对象是刚才我们定义的“Chapter”类。

4. 选择Tree Controller的Buildings栏,设置Controller Content组中的Build to项,勾选并选择View Controller,因为我们设置的数据源novel在这里,设置Model Key Path项值为“self.novel”,注意"self."不能少。

5. 选择Outline View的Buildings栏,设置Outline View Content组中的Build to项,勾选并选择Tree Controller,即上一步我们设置的,Controller Key保持“arrangedObjects”不变;再选择Table View Cell的Buildings栏,设置Outline View Content组中的Build to项,勾选并选择Table Cell View,设置Model Key Path值为“objectValue.title”,注意“objectValue.”不能少。

6. 建立Action,按住“control”键拖到Tree Controller上,在下拉窗口上选择“Add”项,完成该Action的创建;同理建其它两个Action。运行效果如下图:

2995ca046eb1ebfcd04884fc052dd004.png

注意:在这里,“AddChild”功能是不通的,需要你自己去实现。

三、设计目录模块,即将上面所讲的应用到目录模块上。

目录模块的设计需要解决两个问题,即:模块功能、业务逻辑。模块功能说起来比较简单,就是目录节点的增删改与排序。下面先说一个业务逻辑:

A、目录模块的业务逻辑的相关数据与功能:

  • 增。
  1. 新建文件功能。新建文件时,会创建一章一节两个目录节点;
  2. 新建节点功能。新建节点时会新建对应的节点文件。表现为在Ceche文件夹中创建一个以catalog.creation为识别名称的txt文件,作为该节点内容的保存对象,约定名格式为:content2138092789.txt。其中2138092789为当前catalog的creation,注意:改原定前缀chapter为content了,改的原因是我们有章与节两个节点。代码相应的内容都已作更改;
  3. 更新目录、当前章节与已打开章节的数据。首先,为Works.CatalogData添加新节点;其次,重新设置Works的currentContent为当前节点对象,同步Works.InfoData.currentContent为当前节点对象;最后,更新已打开章节的数据,即为Works.InfoData.contentTitleOnBar添加当前节点对象到数组最前,并去重;
  4. 相应界面呈现。首先,CatalogOutlineView中选择到该节点;其次,TabsBarView呈现该节点标题,并为当前状态;最后IdeaTextView显示该节点的信息,ContentView显示该节点内容并获取了焦点,等待用户输入。
  • 删。
  1. 更新目录、当前章节与已打开章节的数据。首先,为Works.CatalogData删除该节点;其次,删除Works.InfoData.contentTitleOnBar对应的元素;最后,如果删除的是Index为0的元素,需要获取新的Index为0的元素,作为Works的currentContent的值及Works.InfoData.currentContent的值。
  2. 相应界面呈现。相应数据更新Works.CatalogData为基础更新CatalogOutlineView,以Works.InfoData.contentTitleOnBar为基础更新TabsBarView,以Works.InfoData.currentContent为基础更新IdeaTextView、ContentTextView;
  3. 删除节点文件。按约定名删除。
  • 改。
  1. 更新目录、当前章节与已打开章节的数据。首先,为Works.CatalogData修改该节点Catalog.title;其次,修改Works.InfoData.contentTitleOnBar对应的元素;最后,如果修改的是Index为0的元素,则同步修改Works.InfoData.currentContent的值。
  2. 相应界面呈现。相应数据更新Works.CatalogData为基础更新CatalogOutlineView,以Works.InfoData.contentTitleOnBar为基础更新TabsBarView。
  • 排序。
  1. 相应数据更新。首先,为Works.CatalogData修改该节点顺序;
  2. 相应界面呈现。相应数据更新Works.CatalogData为基础更新CatalogOutlineView。

B、目录功能实现的自定义视图的支持。

  • 新添章、节的对话框。
从上面可以看出,我们的目录支持的是两级结构,分别对应为Chapter(章)与Section(节)。所以在目录添加时,我们使用了对话框,让用户去选择创建的是Chapter还是Sction。
  1. 新建一个Windows文件夹;
  2. 需要新建一个CatalogWindowController类继承于NSWindowController,并放在Windows文件夹里,文件名为CatalogWindowController.swift。
  3. 建立NSWindowController时,Xcode会为我们准备一个对应的.xib文件,叫CatalogWindowController.xib。像Main.storyboard一样,点击后打开所见即所得的窗体设计。
  4. 我们可以在ViewController.swift中实现一个CatalogWindowController,然后可以通过下列代码显示它。为什么会有delegate呢?是因为窗体设置好了数据,需要ViewController.swift知道并进行下一步的处理。
catalogWindowController 
  • 标题标签与标签栏。
我们还借用了Web上的Tab功能,用在TitleTabsView的显示上,所以我们需要自己定义一个TabsView,Xcode提供的不符合原型的要求,这是我们自己要设计的原因。看到了没,产品经理多厉害。同时为了管理多个Tab,我们又设计了TitleTabsView。
  1. TitleTab是由显示标题的Label与关闭的按钮组成;
  2. 我们需要建立一个TitleTabView类,继承于NSView,并保存在Views文件夹里,名称为TitleTabView.swift;
  3. 为了学习.xib在视图中的应用,我就没有用代码写。于是新建一个同名的.xib,保存在NSView里。然后,添加Label与Button,设置好束备用;
  4. 这个时候该注意了。需要将Document Outline中Custom View上的File's Owner的Identify的Class项设置为TitleTabView。然后建立包括Custom View在内的TitleTabView.swift的Outlet,分别为view、label、button。
  5. 建立两个事件,分别响应tab被点击与关闭。
  6. 最后建立TabsBarView类,放在Views文件夹里。接收Catalog数组,显示Tabs。
  • 右键菜单。
  1. 首先,需要在main.Storyboard里,拖一个菜单到View Controller Scene上,设计好它:
    1. Add,打开当前章节;
    2. Move Up,上移一步;
    3. Move Down,下移一步;
    4. Move to First,移为最前;
    5. Move to Last,移为最后;
    6. Delete,删除该章节。
  2. 然后选择Catalog Outline View的Connections栏的menu项的右边加号上,将其拖到设计好的菜单上,注意是整个菜单。
  3. 最后,为各项建立ViewController.swift的Action,这样就完成了右键菜单设计。

C、目录功能实现的自定义视图的支持,注意我们为Catalog添加了sub属性,已表现为树结构。

由于页面布局上存在6个Outline View,所以在实现NSOutlineViewDelegate、NSOutlineViewDataSource与NSTextFieldDelegate协议时,都需要通过Switch分成6路处理,分别以各视图名为头。以NSOutlineViewDelegate其中一个方法为例代码实现如下:
// 
  • CatalogOutlineView的数据关联与呈现
我们需要将Works.catalogData作为CatalogOutlineView的数据源,选择实现NSOutlineViewDelegate、NSOutlineViewDataSource中的协议,来实现CatalogOutlineView的呈现;
/**
  • CatalogOutlineView的选择与修改。
实现用了NSOutlineViewDelegate、NSTextFieldDelegate的协议。
// NSOutlineViewDelegate    
    
  • CatalogOutlineView的排序。
  1. 目录项移动前原位置数据。
需要保存目录项移动前原位置数据,设计了DragItem、DragItems。
/**

2. 节点的控制是通过处理原数据来实现。

节点的控制方法需要建立对应的测试单元。
/// 

3. 拖移需要实现NSOutlineViewDataSource支持的拖移的三个协议方法。我们像前面一样通过switch,进行分流处理。

在CatalogOutlineView排序中,我们实现的方式是:章只能在章的级别中移动,节只能在节的级别中移动。
/// 结束拖移,章在章间移动、节在节间移动
    

四、标题标签栏排序的实现。

  • 标题标签栏排序的实现。
  1. 重写TitleTabView的,mouseDown、mouseDragged、mouseUp,在mouseDown中记录标签的原位置,在mouseDragged中更新标签位置用来同步鼠标位置,在mouseUp中获取最终位置,用来进行重新排序。
  2. 结果是不错,只是标签在当前层中移动,会被上层的标签遮住,这样肯定不行。我以为这个难不到我,可以通过bringSubviewToFront()方法移到最顶层,但不能想像的是,macOS开发中不支持这个方法。
  3. 后来使用的方法是,使用了两层标签,上层为替身标签。mouseDown时,藏其它替身标签,移动当前替身标签,最终是实现了在最顶层移动标签,但这个花了我大把时间。
  • 最终实现。
  1. 修改TitleTabView,删除了mouseDown、buttonClick方法,同时作了以下修改。
//

2. 添加StandinTabView类继承于TitleTabView。并重写TitleTabView的,mouseDown、mouseDragged、mouseUp。

//

3. 在TabsBarView中实现移动与排序。

/// 实现标签的委托方法。

目录模块的设计到这里算是完成了。

  • “iWriter”原码与素材,请访问我的Github的iWriter工程。

下一篇,文本模块的实现。

让我们在这里,遇见明天的自己!姜友华

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值