Overriding Default Views

While the Lightwell SDK provides a default initialization of views that does its best to match the visual design from the editor, sometimes a view will need more custom handling for a specific use case. To override the default behavior there are a few different options. To learn more about the default types check out LWKLayerInfo.toView(...).

Document reading starts with a loading context:

// Initialize context
let context = LWKLoadingContext(screenName: "signup form")

// Add all views
context?.views.forEach { self.view.addSubview($0) }

// Or add specific view
if let view = context?.view(for: "email") {
    self.view.addSubview(view)
}

Changing the View Type

To change the type we can provide and alternative using LWKLoadingContext.setViewType(_:for:). For example, if we wanted a text input instead of the defaulted label we could do the following:

let context = LWKLoadingContext(screenName: "signup form")

// Set view type
context?.setViewType(UITextView.self, for: "email")

Explicit View Loading

The loading context implicitly loads the views whenever we pull a view out from the context – using LWKLoadingContext.views or LWKLoadingContext.view(for:). We can also explicily load the views:

let context = LWKLoadingContext(screenName: "signup form")

// Load views
context?.loadViews()

Overriding View Loading

We can use LWKLoadingContext.loadViews(usingGenerator:)‘s optional block paramter to read the layer info directly and override the default translation of the design info into native views:

context?.loadViews(usingGenerator: { (layerInfo) -> UIView? in
    let view = layerInfo.toView()

    if layerInfo.name == "form background" {
        view.backgroundColor = UIColor(red: 51.0/255.0, green: 186.0/255.0, blue: 231/255.0, alpha: 1.0)
    }

    return view
})

If we wanted to mix views initialized from Lightwell and views already initialized programatically, we can pass them into the generator to preserve the view hierarchy and action targeting:

context?.loadViews(usingGenerator: { (layerInfo) -> UIView? in
    if layerInfo.name == "map placeholder" {
        // Return view initialized outside of Lightwell
        return mapView
    }
    return layerInfo.toView()
})

We can also use the LWKLoadingContext.loadViews(usingGenerator:) function to manually translate parts or the entirety of the design into native views:

context?.loadViews(usingGenerator: { (layerInfo) -> UIView? in
    // Manually initialize email field as an input
    if layerInfo.name == "email" {
        // Initialize view
        let textField = UITextField()

        // Set content specific properties
        if case let .text( textContent ) = layerInfo.content {
            textField.attributedText = textContent.attributedText
            textField.font           = textContent.font
            textField.textColor      = textContent.textColor
            textField.textAlignment  = textContent.alignment
        }

        // Set geometry
        textField.center            = layerInfo.position
        textField.bounds.size       = layerInfo.size
        textField.layer.anchorPoint = layerInfo.anchorPoint
        textField.transform         = layerInfo.transform
        textField.alpha             = layerInfo.alpha

        return textField
    }
    return layerInfo.toView()
})

We can also use this function to skip over parts of the document we don’t want:

context?.loadViews(usingGenerator: { (layerInfo) -> UIView? in
    if layerInfo.?.contains("mockup") ?? false {
        return nil
    }
    return layerInfo.toView()
})

Additional Considerations

The LWKLoadingContext uses the views returned by the LWKLoadingContext.loadViews(usingGenerator:) as the assigned targets for the actions returned by LWKLoadingContext.action(for:). Because of this, if we wanted to import only the animations from a Lightwell document and didn’t want to manually change the targets for every single reference, we can ignore the layer info and simply return our preexisting views.

context?.loadViews(usingGenerator: { (layerInfo) -> UIView? in
    if layerInfo.name == "background" {
        return backgroundView
    }
    if layerInfo.name == "map" {
        return mapView
    }
    if layerInfo.name == "animating pin" {
        return pinView
    }
    return nil
})

A more in depth look at importing and customizing animations and actions is coming soon.

Looking for Feedback

We are actively developing alternatives to names and string comparisons for identifying screen components. If you have preferences, concerns, or would like to know more about what’s upcoming, let us know.

The API is currently in beta and everything inside of these docs will be changing to improve the SDK. If there is a change you’d like to see or have any feedback, please email us at dev@lightwell.pro.