Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Knot is lightweight & predictable state driven node extension library for Texture(AsyncDisplayKit)

License

Notifications You must be signed in to change notification settings

GeekTree0101/Knot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

23 Commits

Repository files navigation

CI Status Version License Platform

Intro

  • Lightweight & Predictable (Rx dep only)
  • You can easy to separate presentation logic from business logic.
  • KnotState will make your presentation logic as reusability.
  • Support a disposeBag (Just inherit knotable, you don't needs make a disposeBag property :) )
  • Efficient updating node layout with state stream.

Quick Example

Node

class Node: ASDisplayNode & Knotable {
 
 struct State: KnotState {
 
 var title: String
 var subTitle: String
 
 static func defaultState() -> State {
 return .init(title: "-", subTitle: "-")
 }
 }
 
 private enum Const {
 static let titleStyle: StringStyle = .init(.font(UIFont.boldSystemFont(ofSize: 30.0)), .color(.gray))
 static let subTitleStyle: StringStyle = .init(.font(UIFont.boldSystemFont(ofSize: 20.0)), .color(.lightGray))
 }
 
 let titleNode = ASTextNode.init()
 let subTitleNode = ASTextNode.init()
 
 override init() {
 super.init()
 automaticallyManagesSubnodes = true
 }
 
 public func update(_ state: State) {
 
 titleNode.update({
 0ドル.attributedText = state.title.styled(with: Const.titleStyle)
 })
 
 subTitleNode.update({
 0ドル.attributedText = state.subTitle.styled(with: Const.subTitleStyle)
 })
 }
 
 override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
 
 let stackLayout = ASStackLayoutSpec.init(
 direction: .vertical,
 spacing: 20.0,
 justifyContent: .center,
 alignItems: .center,
 children: [
 titleNode,
 subTitleNode
 ]
 )
 
 return ASInsetLayoutSpec.init(
 insets: .zero,
 child: stackLayout
 )
 }
}

Controller

final class ViewController: ASViewController<ASDisplayNode> {
 
 let testNode = Node.init()
 
 let disposeBag = DisposeBag()
 
 init() {
 super.init(node: .init())
 self.title = "Knot"
 self.node.backgroundColor = .white
 self.node.automaticallyManagesSubnodes = true
 self.node.layoutSpecBlock = { [weak self] (_, _) -> ASLayoutSpec in
 guard let self = self else { return ASLayoutSpec() }
 return ASCenterLayoutSpec.init(
 centeringOptions: .XY,
 sizingOptions: [],
 child: self.testNode
 )
 }
 
 Observable<Int>
 .interval(DispatchTimeInterval.milliseconds(100), scheduler: MainScheduler.instance)
 .delaySubscription(DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance)
 .pipe(to: testNode, {
 var (integer, state) = 0ドル
 state.title = "\(integer)"
 return state
 })
 .disposed(by: disposeBag)
 
 Observable<Int>
 .interval(DispatchTimeInterval.milliseconds(10), scheduler: MainScheduler.instance)
 .delaySubscription(DispatchTimeInterval.seconds(1), scheduler: MainScheduler.instance)
 .filter(with: testNode, {
 return 0ドル.0 < 100
 })
 .pipe(to: testNode, {
 var (integer, state) = 0ドル
 state.subTitle = "\(integer)"
 return state
 })
 .disposed(by: disposeBag)
 }
 
 required init?(coder aDecoder: NSCoder) {
 fatalError("init(coder:) has not been implemented")
 }
}

API Guide

Knotable

Knotable will make your node as predictable state driven node

Knotable & KnotState

By inheriting Knotable, you can design as a responsive node.

Example

class Node: ASDisplayNode & Knotable {
 let titleNode = ASTextNode()
 
 struct State: KnotState { // 1. inherit Knot State protocol
 
 var displayTitle: String
 
 static func defaultState() -> State {
 // 2. return defaultState from static defaultState method
 }
 }
 
 // 3. Use a state with updateBlock or as you know
 public func update(_ state: State) {
 
 // using updateBlock
 titleNode.update({
 0ドル.attributedText = NSAttributedString(string: state.displayTitle)
 })
 
 // or as you know
 titleNode.attributedText = NSAttributedString(string: state.displayTitle)
 
 // you don't needs call setNeedsLayout: :)
 }
}

State objects can be separated to the outside.

Example

struct SomeState: KnotState {
 
 var displayTitle: String
 
 static func defaultState() -> State {
 return .init(displayTitle: "-")
 }
}
class Node: ASDisplayNode & Knotable {
 typealias State = SomeState
 let titleNode = ASTextNode()
 public func update(_ state: State) {
 
 titleNode.update({
 0ドル.attributedText = NSAttributedString(string: state.displayTitle)
 })
 }
}

Sink

You can set state directly as sink:

let node = KnotableNode()
node.sink(State.init(...))

Stream

You can set state from observable with stream property. In this case, you don't needs call setNeedsLayout :)

Observable.just(State.init(...)).bind(to: node.stream)

ObservableType convenience extension APIs

pipe(to: KnotableNode)

If observable or subject element is KnotSate then you can just use pipe(to:) it equal to bind(to: knotableNode.stream)

 let node: Knotable & SomeNode = .init(...)
 Observable.just(State.init(...))
 .pipe(to: node)
 .disposed(by: disposeBag)
 
 // equal 
 
 Observable.just(State.init(...))
 .bind(to: node.stream)
 .disposed(by: disposeBag)

Alternatively, You can reduce knotable node state with event

 Observable.just(100)
 .pipe(to: node, {
 var (event, state) = 0ドル
 state.count = event
 return state
 })
 .disposed(by: disposeBag)

filter(with: KnotableNode)

You can filter event with node state

 Observable.just(100)
 .filter(with: node, { (event, state) -> Bool in
 return event == state.count
 }
 //...

withState(from: KnotableNode)

You can get state with event

 Observable.just(100).withState(from: node) // 100, state

state(from: KnotableNode)

You can get state without event

 Observable.just(100).state(from: node) // state

Example

let testNode = TestNode()
testNode.rx.tap
 .state(from: testNode)
 .subscribe(onNext: { state in 
 // TODO
 })
 .disposed(by: testNode.disposeBag)

Requirements

  • Xcode 10.x
  • Swift 5.x
  • RxSwift/Cocoa 5.x

Installation

Knot is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Knot'

Author

Geektree0101, h2s1880@gmail.com

License

Knot is available under the MIT license. See the LICENSE file for more info.

About

Knot is lightweight & predictable state driven node extension library for Texture(AsyncDisplayKit)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /