SCADE

List control

Version 1.0 - 3. February 2017

šŸ“˜

Sourcecode

Please see the UgListControlDemo project for the source code created in this chapter. See Installing examples for information on how to download the examples from https://github.com/scadedoc/UgExamples/tree/master/UgListControlDemo

Introduction

Our list control is very powerful because it's very versatile. You can configure it to do many things.

  • Its can contain list elements of different types
  • Each list element can be composed of many other elements
  • You can hide and show certain elements programmatically
  • You can change a lot of the characteristics by simply binding it to control variables
  • The list control uses native graphics as well as platform specific behaviour, i.e
    • Controls within the list control such as label or button are mapped to its respective native iOS and Android platform
    • Drag and drop & slide or any touch movement use the algorithms of the respective native iOS and Android platform and are native

Using the list control, you can

  • Build a chat app where one list entry type represents messages while other type represents group delimiters
  • Build a eReader app that displays multiple books in different categories using horizontal scrolling
  • Build a banking app to display the accounts using sophisticated fonts and pixel perfect arrangements

Displaying an array in a list control

In the following chapter, we will demonstrate

  1. How to insert the list control into a page
  2. Create a Swift class to represent an array element
  3. Create model property / variable that contains array in the page class
  4. How to bind the array to the list control

Insert a simple list control and create a Swift class to represent list elements

ā€‹ a . Create a new app and open a page, for instance the main page (not shown here)
ā€‹ b. Drag and drop the list control onto the main page (not shown here)
ā€‹ c. Use New / āŒ˜N | Swift and create a new swift class Dog.swift

  • Import ScadeKit and make sure you inherit from EObject
  • Please assign types to the variables, so that the SCADE IDE can work with the classes more intelligently

Add model variable in page

Add a variable that holds the list of dogs and populate the list

  • add the variable dogs of type [Dog] as a @objc dynamic variable to the main.page.swift
  • add a method that populates the dog array
import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {

    @objc dynamic var dogs : [Dog] = []
    
    // page adapter initialization
    override func load(_ path: String) {        
        super.load(path)
        self.setupDogs()
    }
    
    func setupDogs() {
        dogs.append(Dog(id:"d100",name:"Hector",breed:"Boxer",ageInYears:2))
        dogs.append(Dog(id:"d101",name:"Max",breed:"Labrador", ageInYears:3))
        dogs.append(Dog(id:"d102",name:"Bailey",breed:"St.Bernard", ageInYears:3))
    }
}

Add labels to the list control & bind model

Now, lets bind the model (the array) to the list control. This steps creates a relationship between control and array

and link the variables to the labels using the binding tab.

  • Click on the binding tab
  • Expand the model tree on the left side. Main/dogs
  • Link the array structure we iterate over to the items node available below the list control list1

Now we specified that we will iterate over the array structure. Finally, we bind the class properties to the text properties of the labels. We called the labels dogName and dogId

Run the app

Running the app the the simulator displays your list of dogs. Not super pretty yet, but a short and simple demonstration of the list control:

Add handler to process row click / item selected event

To add logic whenever a row is clicked,

  1. get a handle on the list control and cast the object to SCDWidgetsList
  2. add the event handler to the .onItemSelected
  3. The event handler is called SCDWidgetsItemSelectedEventHandler
// listen to itemSelected events
let listControl = self.page!.getWidgetByName("list1") as! SCDWidgetsList
listControl.onItemSelected.append(SCDWidgetsItemSelectedEventHandler{ ev in self.rowClicked(event: ev!)})
  1. The event it fires is SCDWidgetsItemEvent
  2. The item is contained in a field called item
  3. Just cast it to the target structure
func rowClicked(event:SCDWidgetsItemEvent) {
    if let dog = event.item as? Dog {
        print("Hello \(dog.name)")
 }
}

Entire class looks like this

import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {

    @objc dynamic var dogs : [Dog] = []
    
    // page adapter initialization
    override func load(_ path: String) {        
        super.load(path)
        
        self.setupDogs()
        
        // listen to itemSelected events
        let listControl = self.page!.getWidgetByName("list1") as! SCDWidgetsList
        listControl.onItemSelected.append(SCDWidgetsItemSelectedEventHandler{ ev in self.rowClicked(event: ev!)})
                
        // Listen to action buttons
        let listControlRowActionButtonRight = self.page!.getWidgetByName("btnListRowInfo") as! SCDWidgetsButton
        listControlRowActionButtonRight.onClick.append(SCDWidgetsEventHandler{_ in print("Info button Clicked")})
        
        // wire toolbar buttons
        let groupedByButton = self.page!.getWidgetByName("itmGroupedByBreed") as! SCDWidgetsClickable
        groupedByButton.onClick.append(SCDWidgetsEventHandler{_ in self.navigation!.go("groupedByBreed.page")})
        
        
    }
    
    func setupDogs() {
        dogs.append(Dog(id:"d100",name:"Hector",breed:"Boxer",ageInYears:2))
        dogs.append(Dog(id:"d101",name:"Max",breed:"Labrador", ageInYears:3))
        dogs.append(Dog(id:"d102",name:"Bailey",breed:"St.Bernard", ageInYears:3))
    }
    
    func rowClicked(event:SCDWidgetsItemEvent) {
        if let dog = event.item as? Dog {
            print("Hello \(dog.name)")
        }
    }
}

Works nicely:

Add swipe support

Swipe support allows you to display custom areas containing buttons etc when you swipe the list row left and right. We describe adding a button to the right hand side of the listelement:

  1. Press the > arrow to open up the panel on the right
  1. You then can add any control, for instance a button that we will call btnListRowInfo with the title Info. We change background and font color:

All you need to do is to wire up the button. That's pretty easy:

let listControlRowActionButtonRight = self.page!.getWidgetByName("btnListRowInfo") as! SCDWidgetsButton
listControlRowActionButtonRight.onClick.append(SCDWidgetsEventHandler{_ in print("Info button Clicked")})

Add grouping separator to list control

Adding a separator row to indicate the start of a new set of data is a common use case:

We use the power and flexibility of the list control to add a seperator row. We insert two horizontal layout grids to the list element, and then add the labels indicating the group value and the data details in the respective grid:

The way to achieve this is to hide the elements that form the separator line when the dog's data need to be displayed, and hide the Dog data when the group view needs to be displayed. Therefore, we put all group separator related elements in viewGroup and all detail related elements into viewDetails.

In order to avoid having to code this using Swift code, we add two variables isGroup and isDetail that indicate if the item is a group separator or if it is a detail row:

import ScadeKit

class DogView : EObject {
        
    // please make sure you annotate each variable with the type
    // for SCADE to more identify and leverage the type information
    let id : String
    let name : String
    let ageInYears : Int 
    let breed:String 
    
  // use to indicate if its a separator item or detail item
    let isGroup:Bool
    let isDetail:Bool
    
    init(id:String,name:String,breed:String,ageInYears:Int) {
        self.id = id
        self.name = name
        self.breed = breed
        self.ageInYears = ageInYears    
        self.isGroup = false
        self.isDetail = !self.isGroup
    }
    
    init(breed:String) {
        self.id = ""
        self.name = ""
        self.breed = breed
        self.ageInYears = -1    
        self.isGroup = true
        self.isDetail = !self.isGroup
    }
}

To hide any layout grid, we need to set two attributes, Layout.exclude and Layout.visible:

  • we set Layout.exlude to true and
  • we set Layout.visible to false

We do this using our binding editor:

Finally, we only need to supply the data item:

import ScadeKit

class GroupedByBreedPageAdapter: SCDLatticePageAdapter {

    @objc dynamic var dogs : [DogView] = []
    
    // page adapter initialization
    override func load(_ path: String) {        
        super.load(path)
        setupDogs()
        
        let backbutton = self.page!.getWidgetByName("itmDoglist") as! SCDWidgetsClickable
        backbutton.onClick.append(SCDWidgetsEventHandler{_ in self.navigation!.go("main.page")})
    }
    
    func setupDogs() {
        dogs.append(DogView(breed:"Boxer"))
        dogs.append(DogView(id:"d100",name:"Hector",breed:"Boxer",ageInYears:2))
        dogs.append(DogView(breed:"Labrador"))
        dogs.append(DogView(id:"d101",name:"Max",breed:"Labrador", ageInYears:3))
        dogs.append(DogView(id:"d102",name:"Moritz",breed:"Labrador", ageInYears:3))
        dogs.append(DogView(breed:"St.Bernard"))
        dogs.append(DogView(id:"d103",name:"Bailey",breed:"St.Bernard", ageInYears:3))
    }
}

Now, we grouped our dogs by breed, with full design control of the grouping row.

Create clicked animation effect on list element

This chapter explains on how to add a fade animation effect that visualizes that a row has been clicked. After clicking a row, the background color with turn to gray and that gray color with slowly fade:

  1. Create a new project and add a list control into the main page. Add a label into the list
  2. Create a reference to the list control and add an event handler when the list row is clicked
// create reference to list
let list1 = self.page!.getWidgetByName("list1") as! SCDWidgetsList
        
// populate with some items so that the list is not empty
list1.items = [1,2,3]
        
// add event handler when list row is clicked
list1.onItemSelected.append(SCDWidgetsItemSelectedEventHandler{event in ... })
  1. Each list consists of a list of SCDWidgetsListElement that represent an individual list item. The element variable in the event contains the reference to the element
// reference the SCDWidgetsListElement that has been clicked
let listElement = event!.element as! SCDWidgetsListElement
  1. Now we change the list element's background color. Once the code is executed, the list element becomes gray
// set background color (yes, this API is wordy, we will improve it)
let backgroundStyleClass = SCDWidgetsBackgroundStyle()
let style = listElement.getStyle(backgroundStyleClass.eClass()) as! SCDWidgetsBackgroundStyle
style.type = SCDWidgetsBackgroundType.color
style.color = self.grayColor // previously defined
  1. Now the most important part. Get background rectangle inside list element:. Each list element is represented as a SVGGroup, which in turns contains a box which holds a rectangle. The rectangle is the correct element to modify to change the background:
// find the graphical area (the rectangle) that represents tha background
let drawingGroup = (listElement.drawing) as! SCDSvgGroup
let box = drawingGroup.drawableChilds[0] as! SCDSvgBox
let rect = box.drawableChilds[0] as! SCDSvgRect
  1. Finally, we add an animation that changes the opacity, as such giving a fading effect. (Alternatively, you could have changed the color itself). The animation guide contains a lot more details on animation:
// create animation to change the opacity. See animation guide for more details
let anim = SCDSvgPropertyAnimation("fillOpacity", values: [1, 0.5, 0])
anim.duration = 0.4
anim.repeatCount = 1
anim.delay = 0.2

Please see the full source code below

import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {

 let grayColor = SCDGraphicsRGB(red: 211, green: 211, blue: 211)
 
 // page adapter initialization
 override func load(_ path: String) {       
  super.load(path)
  
        // create reference to list
    let list1 = self.page!.getWidgetByName("list1") as! SCDWidgetsList
        
    // populate with some items so that the list is not empty
    list1.items = [1,2]
        
    // add event handler when list row is clicked
    list1.onItemSelected.append(SCDWidgetsItemSelectedEventHandler{event in
            
              // reference the SCDWidgetsListElement that has been clicked
              let listElement = event!.element as! SCDWidgetsListElement

              // set background color (yes, this API is wordy, we will improve it)
              let backgroundStyleClass = SCDWidgetsBackgroundStyle()
              let style = listElement.getStyle(backgroundStyleClass.eClass()) as! SCDWidgetsBackgroundStyle
              style.type = SCDWidgetsBackgroundType.color
              style.color = self.grayColor

              // find the graphical area (the rectangle) that represents tha background
              let drawingGroup = (listElement.drawing) as! SCDSvgGroup
              let box = drawingGroup.drawableChilds[0] as! SCDSvgBox
              let rect = box.drawableChilds[0] as! SCDSvgRect

              // create animation to change the opacity. See animation guide for more details
              let anim = SCDSvgPropertyAnimation("fillOpacity", values: [1, 0.5, 0])
              anim.duration = 0.4
              anim.repeatCount = 1
              anim.delay = 0.2

              // adding the animation starts it
              rect.animations.append(anim)
    
  })
}
}

Result:

Updated 2 years ago

List control


Version 1.0 - 3. February 2017

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.