React激发的UIKit组件 – Komponents

Komponents

Komponents is a Swift framework for building component-oriented interfaces.

Because it’s unfair to need javascript to enjoy Components

Building a Loading screen with Hot reload

func render() -> Node {
    return Label("Hello, Component !")
}

New to components? Fear not! Facebook’s React guide is a gold mine of information to get you started 🙂

A component is pretty simple : A Bare Component

  • It has arenderfunction that returns aNode.
  • It has astateproperty.

That’s All!

import Komponents

class MyFirstComponent: Component {

    var state = MyState()

    func render() -> Node {
        return Label("Hello!")
    }
}

View Controller Component

To use a component as aUIViewControllerand play nicely with UIKit apis, just subclassUIViewControllerand callloadComponentinloadView:)

class LoadingScreen: UIViewController, Component {

    // Just call `loadComponent` in loadView 🙂
    override func loadView() { loadComponent() }

    func render() -> Node {
        return ...
    }
}

You can nowpushandpresentyour view controller like you used to, except this is now a powerful component!��

View Component

This is particularly handy to start migrating parts of the App to using components without breaking everything! To use a component as aUIViewand play nicely with UIKit apis, just subclassUIViewand callloadComponentin aninitfunction 🙂

class MyCoolButton: UIView, Component {

    // Here we load the component
    convenience init() {
        self.init(frame:CGRect.zero)
        loadComponent()
    }

    func render() -> Node {
        return ...
    }
}

This way you have classicUIViewthat behaves like a component!��

Nodes

The idea is that you will compose your own components out of the default nodes provided.

The naming is pretty simple, take the UIKit name and removeUIprefix.UIViewbecomesView,UIButtonButton, etc… you get it 🙂

Here is the complete list of provided nodes :

View

Label

Field(TextField)

TextView

Button

Image

ScrollView

PageControl

ActivityIndicatorView

VerticalStack

HorizontalStack

Slider

Switch

ProgressView

Left to implement (coming soon)

SegmentedControl Stepper TableView CollectionView TableViewCell CollectionViewCell DatePicker PickerView VisualEffectView MapKitView Webview TapGestureRecognizer PinchGestureRecognizers RotationGestureRecognizers SwipeGestureRecognizers Toolbar SearchBar

Customizing a Node

Every node follows the same convention :

Node(ContentType,
     style: ((NodeType) -> Void),
     layout: ((NodeType) -> Void),
     ref: UnsafeMutablePointer<NodeType>,
     children: [Renderable])

For example aLabel:

Label("My first Label",
     style: { $0.textColor = .red },
     layout: { $0.centerInContainer() },
     ref:  &lablel,
     children: [
        // Add Child nodes here
     ])

What’s cool is that everything is optional so that you can just write :

Label("My first Label")

Content

The first parameter is the content, for instance an image for anImagenode, some text for aLabel.

Style

The style parameter is where you customize theUIKitobject. Just style your object however you want, say modify it’sbackgroundColor!

Layout

Notice we strongly separated thelayoutand thestyleparameters. Though they are both called with the UIKit object, we believe it’s clearer to seperate what is styling from what is in the layout domain.

Once again we want to play nice with UIKit and in this block you can use pure Autolayout . We want to stress this point because it’s a major advantage of this tool. We like to use Stevia , but be free to use whatever layout library that suits your needs !

Ref

This will come very useful for falling back to the classic way of doing things.

class MyComponent: StatelessComponent {

    var labelReference = UILabel()

    func render() -> Node {
        return Label("My text", ref: &labelReference)
    }

    func foo() {
      // Later you can access the label and fall back to the old way of doing thing!
      labelReference.text = "Yay I'm not stuck in this !"
    }
}

Children

This is just an array of the nested nodes.

View-Wrapped Component

Display your component in a UIView and use it wherever You want!

let view = ComponentView(component: MyComponent())

ViewController-Wrapped Component

Embbed your component in view Controller and present it anyway you want 🙂

let vc = ComponentVC(component: MyComponent())

Looping !

func render() -> Node {
    let items = ["Hello", "How", "Are", "You?"]
    return
        View(style: { $0.backgroundColor = .white }, [
            VerticalStack(style: { $0.spacing = 40 }, layout: { $0.centerInContainer() },
                items.map { Label($0) }
            )
        ])
}

Example: A Loading Screen

import UIKit
import Stevia
import Komponents

class LoadingScreen: UIViewController, StatelessComponent {

    override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }

    override func loadView() { loadComponent() }

    func render() -> Node {
        return
            View(style: { $0.backgroundColor = .darkGray }, [
                HorizontalStack(style: { $0.spacing = 8 }, layout: {$0.centerInContainer() }, [
                    Label("Loading...", style: { $0.textColor = .white }),
                    ActivityIndicatorView(.white, style: { $0.startAnimating() })
                ])
            ])
    }
}

More Examples

Take a look at the example ProjectKomponentsExample.xcodeprojand play around !

Patching

Like React, Komponents tries to be smart about what it rerenders when the state changes.

By default only the component that changed is re-rendered. Say you have the nested componentsA>B>CandC’s state changes, onlyCis going to rerender.

But the whole C component is going to be re-rendered, and maybe only some text, or some color changed, and the structure of it is pretty much the same. That’s where patching comes in!

Patching is going to rerender the component in a background thread, compare the new view hierarchy with the old one, and batch the updates on the UIThread.

Patching is still experimental but can only get better with time!

You can opt-in by implementingenablePatching()in your component :

func enablePatching() -> Bool {
  return true
}

Supported patching properties :

View:backgroundColor,isHidden,alpha

Label:text,textColor

Image:image

Button:title(state normal),backgroundImage(state normal),isEnabled

Installation

Carthage

github "freshOS/Komponents"

Manually

Simply Copy and Paste all the.swiftfiles from theSourcefolder in your Xcode Project 🙂

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:React激发的UIKit组件 – Komponents