-
Notifications
You must be signed in to change notification settings - Fork 261
New Provider #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@dillidon Yes, actually this sounds great! The only drawback with this is that it adds another layer of complexity to the user. there is ComposedProvider <- BoxProvider <- Box all feed into one another. So here is what I'm thinking if a Box and provide a section as well and BoxProvider basically takes on the job of the ComposedProvider. It would be really nice. |
finally, if you use this code: public protocol BoxBase: CollectionReloadable { }
public protocol BoxType: BoxBase {
var id: String { get set }
func view() -> UIView
func update(view: UIView, provider: BoxProvider)
func size(at index: Int, collectionSize: CGSize, provider: BoxProvider) -> CGSize
func didTap(view: UIView, at index: Int, provider: BoxProvider)
}
public extension Array where Element == BoxType {
public func index(of item: Element) -> Int? {
return index(id: item.id)
}
public func index(id: String) -> Int? {
print(self.map {$0.id})
for index in 0 ..< self.count {
if self[index].id == id {
return index
}
}
return nil
}
public func item(at index: Int) -> Element? {
guard index >= 0 && index < count else { return nil }
return self[index]
}
}
public struct BoxViewContext<View: UIView> {
public var view: View
public var provider: BoxProvider
public var base: BoxBase
}
public struct BoxSizeContext<View: UIView> {
public var view: () -> View
public var index: Int
public var contentSize: CGSize
}
public struct BoxTapContext<View: UIView> {
public var index: Int
public var view: View
public var provider: BoxProvider
public var base: BoxBase
}
public class Box<View: UIView>: BoxType {
public typealias ViewSource = (BoxViewContext<View>) -> Void
public typealias SizeSource = (BoxSizeContext<View>) -> CGSize
public typealias TapHandler = (BoxTapContext<View>) -> Void
public private(set) lazy var reuseManager = CollectionReuseViewManager()
public var id: String
internal var viewSource: ViewSource?
internal var sizeSource: SizeSource?
internal var tapHandler: TapHandler?
public init(id: String = UUID().uuidString) {
self.id = id
}
@discardableResult public func view(new: @escaping ViewSource) -> Self {
viewSource = new
return self
}
@discardableResult public func size(new: @escaping SizeSource) -> Self {
sizeSource = new
return self
}
@discardableResult public func tap(new: @escaping TapHandler) -> Self {
tapHandler = new
return self
}
public func view() -> UIView {
return reuseManager.dequeue(View())
}
public func update(view: UIView, provider: BoxProvider) {
guard let view = view as? View else { return }
let context = BoxViewContext(view: view, provider: provider, base: self)
viewSource?(context)
}
public func size(at index: Int, collectionSize: CGSize, provider: BoxProvider) -> CGSize {
let view: () -> View = { [unowned self] in
guard let view = self.view() as? View else {
print("!!! can't get view in BOX")
return View()
}
self.update(view: view, provider: provider)
return view
}
let context = BoxSizeContext(view: view, index: index, contentSize: collectionSize)
return sizeSource?(context) ?? collectionSize
}
public func didTap(view: UIView, at index: Int, provider: BoxProvider) {
guard let tapHandler = tapHandler, let view = view as? View else { return }
let context = BoxTapContext(index: index, view: view, provider: provider, base: self)
tapHandler(context)
}
}
open class BoxDataSource: CollectionReloadable {
open var data: [BoxType] { didSet { setNeedsReload() } }
public init(data: [BoxType] = []) {
self.data = data
}
public init(data: BoxType) {
self.data = [data]
}
open var numberOfItems: Int {
return data.count
}
open func identifier(at: Int) -> String {
return data[at].id
}
open func data(at: Int) -> BoxType {
return data[at]
}
}
open class BoxProvider: ItemProvider, LayoutableProvider, CollectionReloadable {
open var identifier: String?
open var data: [BoxType] {
didSet {
setNeedsReload()
setNeedsInvalidateLayout()
}
}
open var layout: Layout { didSet { setNeedsInvalidateLayout() } }
open var animator: Animator? { didSet { setNeedsReload() } }
public init(id: String? = nil,
data: [BoxType] = [],
layout: Layout = FlowLayout(),
animator: Animator? = nil) {
self.identifier = id
self.data = data
self.layout = layout
self.animator = animator
}
open func identifier(at index: Int) -> String {
return data[index].id
}
open var numberOfItems: Int {
return data.count
}
open func view(at index: Int) -> UIView {
return data[index].view()
}
open func update(view: UIView, at index: Int) {
data[index].update(view: view, provider: self)
}
open func layoutContext(collectionSize: CGSize) -> LayoutContext {
return BoxProviderLayoutContext(collectionSize: collectionSize, provider: self, data: data)
}
open func animator(at: Int) -> Animator? {
return animator
}
open func didTap(view: UIView, at index: Int) {
data[index].didTap(view: view, at: index, provider: self)
}
open func hasReloadable(_ reloadable: CollectionReloadable) -> Bool {
return reloadable === self //|| reloadable === data //|| reloadable === sizeSource
}
}
extension BoxProvider {
open func remove(item id: String) {
guard let index = data.index(id: id) else { return }
data.remove(at: index)
}
open func replace(item id: String, with new: BoxType) {
guard let index = data.index(id: id) else { return }
data[index] = new
}
open func replace(item old: BoxType, with new: BoxType) {
guard let index = data.index(of: old) else { return }
data[index] = new
}
open func replace(item old: BoxType, with new: [BoxType]) {
guard let index = data.index(of: old) else { return }
data.remove(at: index)
for (i, box) in new.enumerated() {
data.insert(box, at: index + i)
}
}
}
struct BoxProviderLayoutContext: LayoutContext {
var collectionSize: CGSize
var provider: BoxProvider
var data: [BoxType]
var numberOfItems: Int {
return data.count
}
func data(at: Int) -> Any {
return data[at]
}
func identifier(at: Int) -> String {
return data[at].id
}
func size(at index: Int, collectionSize: CGSize) -> CGSize {
return data[index].size(at: index, collectionSize: collectionSize, provider: provider)
}
}
open class BoxComposedProvider: SectionProvider, LayoutableProvider, CollectionReloadable {
open var identifier: String?
open var providers: [BoxProvider] { didSet { setNeedsReload() } }
open var layout: Layout { didSet { setNeedsInvalidateLayout() } }
open var animator: Animator? { didSet { setNeedsReload() } }
public init(id: String? = nil,
layout: Layout = FlowLayout(),
animator: Animator? = nil,
providers: [BoxProvider] = []) {
self.identifier = id
self.layout = layout
self.animator = animator
self.providers = providers
}
open func identifier(at index: Int) -> String {
return providers[index].identifier ?? "\(index)"
}
open var numberOfItems: Int {
return providers.count
}
open func section(at index: Int) -> Provider? {
return providers[index]
}
open func layoutContext(collectionSize: CGSize) -> LayoutContext {
return BoxComposedProviderLayoutContext(collectionSize: collectionSize, providers: providers)
}
open func animator(at: Int) -> Animator? {
return animator
}
open func willReload() {
for provider in providers {
provider.willReload()
}
}
open func didReload() {
for provider in providers {
provider.didReload()
}
}
open func hasReloadable(_ reloadable: CollectionReloadable) -> Bool {
return reloadable === self || providers.contains(where: { $0.hasReloadable(reloadable) })
}
}
struct BoxComposedProviderLayoutContext: LayoutContext {
var collectionSize: CGSize
var providers: [BoxProvider]
var numberOfItems: Int {
return providers.count
}
func data(at: Int) -> Any {
return providers[at]
}
func identifier(at: Int) -> String {
return providers[at].identifier ?? "\(at)"
}
func size(at index: Int, collectionSize: CGSize) -> CGSize {
providers[index].layout(collectionSize: collectionSize)
return providers[index].contentSize
}
} with this code: struct ViewConfig<T> {
let config: (T) -> Void
}
protocol Configurable {
init()
}
extension UIView: Configurable {}
extension Configurable {
init(config: ViewConfig<Self>) {
self.init()
apply(config: config)
}
@discardableResult
func apply(config: ViewConfig<Self>) -> Self {
config.config(self)
return self
}
@discardableResult
func config(view: (Self) -> Void) -> Self {
view(self)
return self
}
} you can implement as follows extension ViewConfig where T: UITextField {
static var name: ViewConfig<UITextField> {
return ViewConfig<UITextField> {
$0.placeholder = Localized.NAME
$0.font = font
$0.backgroundColor = nil
$0.textColor = .black
$0.clearButtonMode = .whileEditing
$0.autocapitalizationType = .words
$0.keyboardType = .default
$0.spellCheckingType = .no
$0.returnKeyType = .next
}
}
}
let data = Box<UITextField>()
.view { $0.view.apply(config: .name) }
.tap { $0.view.becomeFirstResponder() }
.size { CGSize(width: $0.contentSize.width, height: 57) }
let provider = BoxProvider(data: [data]) |
How does this final code perform the refresh and assignment operations |
Uh oh!
There was an error while loading. Please reload this page.
Hi!
There is a lack of a provider that could contain a data with different types of views, and not just one type.
For example, you can combine view and size w/o data into one box.
This would make it much easier to create such examples as messages). I tested this code and it works with your example 'HorizontalGalleryViewController'.
The text was updated successfully, but these errors were encountered: