This is the documentation for extending UsbongKit, such as adding Nodes, Modules, and Task Nodes.
To add modules, simply create a class and conform it to the Module
protocol. The current modules are located in UsbongKit/Modules/
directory, thus, it is best to add new modules in this directory.
Let's say you want to create a title module, a module that displays text with a header style:
- Create
TitleModule.swift
with contents:
public class TitleModule: Module {
public var title: String
public init(title: String) {
self.title = title
}
}
- Now that you have the module, it's time to create a view for it. All views for the modules are subclasses of
UITableViewCell
s, since they are rendered inside aUITableView
. The views are located inUsbongKit/Views/
. Create the cell subclass,TitleTableViewCell
, and customize it to your needs. If you create a nib (.xib
file), it's best to conform yourUITableViewCell
subclass toNibReusable
. If you're not using a nib, conform instead toReusable
. It will be used later in theNodeView
for registering your cell in theUITableView
. For example:
public class TitleTableViewCell: UITableViewCell, NibReusable {
@IBOutlet public weak var titleLabel: UILabel!
}
- Now that you have the module and its view, you can now add code in
NodeView
to display the module.NodeView
is located inUsbongKit/Nodes/NodeView.swift
. First, register your cell subclass to the table view in thesharedInitialization()
function (registering the cell will allow you to use it in theUITableView
):
tableView.registerReusableCell(TitleTableViewCell)
- You can now add code to display the cell for that module. Find the function
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
. It is located inextension NodeView: UITableViewDataSource
. You can see there that there is a switch statement, and the cases are for determining what type of module is the current module for that row. Go ahead and add a case for yourTitleModule
:
case let titleModule as TitleModule:
let reusedCell = tableView.dequeReusableCell(indexPath: indexPath) as TitleTableViewCell
// Change the title in the view/cell
reusedCell.titleLabel.text = titleModule.title
// Pass the reusedCell to the cell variable, which is the one to be rendered
cell = reusedCell
- That's it! You've created your own module. To display it, you need to add your module to a node. You can look at the example app in
UsbongKit-example/
, to display your node with your module.UsbongKit-example/NodesTableViewController
shows a list of nodes to be displayed. You can add a node with your module in thenodes: [Node]
array (ex.Node(modules: [TitleModule(title: "A Title Module")])
). To have a specific combination of modules in a node, you can create a newNode
subclass.
Creating a Node is faster than creating a module since a Node is simply a collection of modules. Thus, to create a custom node, subclass Node
, and add in your combination of modules in modules
. Let's say you want to create a Node with your TitleModule
and the node you want is a combination of a title followed by a text (TextModule
):
public class TitleAndTextNode: Node {
public init(title: String, text: String) {
super.init(modules: [
TitleModule(title: title),
TextModule(text: text)
])
}
}
You can also add this node to the UsbongKit example app mentioned earlier.
Assuming you've created your custom node and its view for your new task node, you can add a new one in UsbongTree
, which is a class for parsing the utree format.
- To add a task node, the first thing you need to do is add a case in the enum
TaskNodeType
, and its corresponding string (determined by the type in the task-node name). Let's say you added atitleTextDisplay
, which corresponds to yourTitleAndTextNode
:
case TitleTextDisplay = "titleTextDisplay"
- You'll notice that there is an error. It's located in the switch statement of the function
nodeWithName(taskNodeName: String) -> Node
in the classUsbongTree
. It's found inUsbongKit/Usbong/UsbongTree/UsbongTree.swift
. The switch statement now has an error because it doesn't handle all the cases in theTaskNodeType
enum since you've added a new case,TitleAndTextDisplay
.- First let's learn what this function does. In this function, it fetches the task node in the XML with the name inside the
taskNodeName
variable.- You can see a
nameInfo
variable, anXMLNameInfo
object, inside the function. This name info simply decomposes the task-node name into components and provides properties for easier access to images, audios and text determined by the utree format. ThenameInfo.text
is the text of the node which is always the last component in the task node name. ThenameInfo.type
is the task node type (such as textDisplay, imageDisplay, etc.). - The
nodeIndexerAndTypeWithName(taskNodeName)
returns multiple values (called a tuple). The first value is the XML tag, while the second value is theNodeType
, a main type of a task node. There are three main types of a task node:TaskNode
,Decision
, andEndState
. These are determined by the XML tag of the utree format. - The
finalText
variable is a translated and parsed text of the task node.
- You can see a
- First let's learn what this function does. In this function, it fetches the task node in the XML with the name inside the
- Since the type of the task node is in
nameInfo.type
, we create a switch statement for it to apply custom behavior for each task node type. Thus, we add our case there for ourTitleTextDisplay
:
case .TitleTextDisplay:
// Let's assume the title is located in the 2nd component of the task node name
// You can also customize XMLNameInfo to add a property for title
let title = nameInfo.components[1]
// Create the TitleAndTextNode and pass it to the node variable
node = TitleAndTextNode(title: title, text: finalText)
- You've now added a new task node! You can test it by creating a utree with your new task node.