List control

SCADE 2.0 - December 2021

📘

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/UgListControlDemo2

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

Displaying an array in a list control

In this example, we want to display the first 100 most popular dog names (from 2020) together with a breed and id.

Add list control to page

  • We use the contraints box to make sure it uses all the space available

Add controls to list template

We add the controls we want to see in each list element

  • Each list control contains a list of list elements
  • Each list element contains the same visual controls to display the data
  • The list element is designed using the the list controls's list element template
  • In this example, we added 3 text labels into a gridView

Define Data Model

  • We use the Dog class to display a list of dog names and breeds
  • We inherit from EObject
import ScadeKit

class Dog : EObject {

  // please make sure you annotate each variable with the type
  // for SCADE to more identify and leverage the type information
  // currently, you must inherit from EObject
  var id: String
  var name: String
  var ageInYears: Int
  var breed: String

  init(id: String, name: String, breed: String, ageInYears: Int) {
    self.id = id
    self.name = name
    self.breed = breed
    self.ageInYears = ageInYears
  }
}

Bind data to controls

Bind the list elements visual control to the data

  • In order to bind the view to the model, we use the SCDWidgetsElementProvider class
  • Its set using .elementProvider
  • You set an instance of SCDWidgetsElementProvider and add the logic that binds the data to the list element's visual controls
  • The template parameter is of type SCDWidgetsGridView. Use the getWidgetByName method to retrieve a reference to the visual control and set the data from the Dog class instance:
class MainPageAdapter: SCDLatticePageAdapter {

  var dogs: [Dog] = []

  // page adapter initialization
  override func load(_ path: String) {

    super.load(path)

    self.list1.elementProvider = SCDWidgetsElementProvider { (dog: Dog, template) in

      (template.getWidgetByName("dogName") as? SCDWidgetsLabel)?.text = dog.name

      (template.getWidgetByName("dogBreed") as? SCDWidgetsLabel)?.text = dog.breed

      (template.getWidgetByName("dogId") as? SCDWidgetsLabel)?.text = dog.id
                                                         
    }
  }
}

Set the data source of the control

  • Define the source of data by setting the items property
  • Its a collection of data objects
list1.items = Dogs().dogs

List Control lists data successfully

Add handler to process row click / item selected event

To add logic whenever a row is clicked,

  1. Add the event handler to the .onItemSelected
  2. The event handler is called SCDWidgetsItemSelectedEventHandler
// the onItemSelected handler is fired when we click a list's row
list1.onItemSelected.append(
      SCDWidgetsItemSelectedEventHandler { event in 
            if let dog = event?.item as? Dog {
                print("Hello \(dog.name)")
            }
      }
)

Create clicked animation effect on list element

This chapter explains on how to change the background when a row has been clicked.

We add another handler to the onItemSelected handler array. This time, we use the event's element property to access the list's list element SCDWidgetsListElement and set the animation effect

  • We use gold as a start color
  • We use the original color as the end color
  • We create an animation effect. We animate the fill property (to fill the background)
  • We apply the animation effect to the background of the list element
  • Accessing the SVG component of the SCDWidgetsListElement requires understanding of the underlying API. The code to do this is encapsulated in a helper class and the component exposed as backgroundSvgRect
// add animation when list row is clicked
list1.onItemSelected.append(
  SCDWidgetsItemSelectedEventHandler { [weak self] event in

    if let listElement = event?.element as? SCDWidgetsListElement,
      let backgroundColor = listElement.backgroundColor,
      let self = self {
        // We want to animate bacground color from gold to the original color
        let fromColor = SCDSvgRGBColor.gold
        let toColor = self.colorConvert(backgroundColor)

        // Lets animate fill property
        let anim = SCDSvgPropertyAnimation("fill", from: fromColor, to: toColor)
        anim.duration = 0.4
        anim.repeatCount = 1
        anim.delay = 0.2
        anim.deleteOnComplete = true

        listElement.backgroundSvgRect?.animations.append(anim)
      }
})
func colorConvert(_ color: SCDGraphicsRGB) -> SCDSvgRGBColor {
  return SCDSvgRGBColor(red: color.red, green: color.green, blue: color.blue, alpha: color.alpha)
}

Ensure background is set with fill color

The list element's background can be set to a. Nothing b. Image c. FIll Color
If the background is set to Nothing, no visual component exists and the background cannot be set. Therefore, its mandatory that you set the background to a background color:

Access list element's graphic representation

The list element has a background represented by an SVGRect element. To access this element we use the following extension

extension SCDWidgetsWidget {

  var backgroundSvgImage: SCDSvgImage? {
    return self.backgroundSvgElement as? SCDSvgImage
  }

  var backgroundSvgRect: SCDSvgRect? {
    return self.backgroundSvgElement as? SCDSvgRect
  }

  private var backgroundSvgElement: SCDSvgElement? {
    let background = (self.drawing as? SCDSvgContainerElement)?.children[0]
    if let container = (background as? SCDSvgContainerElement), !container.children.isEmpty {
      return container.children[0]
    } else {
      return nil
    }
  }

}

Row selected visual effect video