Merge pull request #233 from TouchInstinct/feature/container_cell_and_base_tap_view_model

Feature/container cell and base tap view model
This commit is contained in:
MRSorokinMaxim 2020-08-20 09:55:16 +03:00 committed by GitHub
commit 37c8d736db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 278 additions and 1 deletions

View File

@ -1,5 +1,11 @@
# Changelog
### 0.9.38
- **Add**: `BaseRxTableViewCell` is subclass of `UITableViewCell` class with support `InitializableView` and `DisposeBagHolder` protocols.
- **Add**: `ContainerTableCell` is container class that provides wrapping any `UIView` into `UITableViewCell`.
- **Add**: `BaseTappableViewModel` is simplifies interaction between view and viewModel for events of tapping.
- **Add**: `VoidTappableViewModel` is subclass of `BaseTappableViewModel` class with void payload type.
### 0.9.37
- **Fix**: ScrollView content offset of `PaginationWrapper` for iOS 13.
- **Fix**: Load more request crash of `PaginationWrapper`.

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "LeadKit"
s.version = "0.9.37"
s.version = "0.9.38"
s.summary = "iOS framework with a bunch of tools for rapid development"
s.homepage = "https://github.com/TouchInstinct/LeadKit"
s.license = "Apache License, Version 2.0"

View File

@ -19,6 +19,10 @@
4CF65D1424DD684A0006B001 /* ButtonHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF65D1324DD684A0006B001 /* ButtonHolder.swift */; };
4CF65D1624DD69250006B001 /* UIButton+ButtonHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF65D1524DD69250006B001 /* UIButton+ButtonHolder.swift */; };
4CF65D1824DD6C080006B001 /* ButtonHolderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF65D1724DD6C080006B001 /* ButtonHolderView.swift */; };
52421F8D24EAB52E00948DD1 /* ContainerTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52421F8C24EAB52E00948DD1 /* ContainerTableCell.swift */; };
52421F8F24EAB84900948DD1 /* BaseRxTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52421F8E24EAB84900948DD1 /* BaseRxTableViewCell.swift */; };
52421F9424EBCFAE00948DD1 /* VoidTappableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52421F9324EBCFAE00948DD1 /* VoidTappableViewModel.swift */; };
52421F9624EBCFBB00948DD1 /* BaseTappableViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52421F9524EBCFBB00948DD1 /* BaseTappableViewModel.swift */; };
67051ADB1EBC7C36008EADC0 /* SpinnerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67051ADA1EBC7C36008EADC0 /* SpinnerView.swift */; };
67051ADD1EBC7C36008EADC0 /* SpinnerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67051ADA1EBC7C36008EADC0 /* SpinnerView.swift */; };
6713C23720AF0C4D00875921 /* NetworkOperationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6713C23620AF0C4D00875921 /* NetworkOperationState.swift */; };
@ -556,6 +560,10 @@
4CF65D1324DD684A0006B001 /* ButtonHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonHolder.swift; sourceTree = "<group>"; };
4CF65D1524DD69250006B001 /* UIButton+ButtonHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+ButtonHolder.swift"; sourceTree = "<group>"; };
4CF65D1724DD6C080006B001 /* ButtonHolderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonHolderView.swift; sourceTree = "<group>"; };
52421F8C24EAB52E00948DD1 /* ContainerTableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContainerTableCell.swift; sourceTree = "<group>"; };
52421F8E24EAB84900948DD1 /* BaseRxTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseRxTableViewCell.swift; sourceTree = "<group>"; };
52421F9324EBCFAE00948DD1 /* VoidTappableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoidTappableViewModel.swift; sourceTree = "<group>"; };
52421F9524EBCFBB00948DD1 /* BaseTappableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTappableViewModel.swift; sourceTree = "<group>"; };
67051ADA1EBC7C36008EADC0 /* SpinnerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinnerView.swift; sourceTree = "<group>"; };
6713C23620AF0C4D00875921 /* NetworkOperationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkOperationState.swift; sourceTree = "<group>"; };
6713C23B20AF0D5900875921 /* NetworkOperationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkOperationModel.swift; sourceTree = "<group>"; };
@ -874,6 +882,39 @@
path = ButtonHolder;
sourceTree = "<group>";
};
52421F8B24EAB52E00948DD1 /* ContainerTableCell */ = {
isa = PBXGroup;
children = (
52421F8C24EAB52E00948DD1 /* ContainerTableCell.swift */,
);
path = ContainerTableCell;
sourceTree = "<group>";
};
52421F9024EAB84E00948DD1 /* BaseRxTableViewCell */ = {
isa = PBXGroup;
children = (
52421F8E24EAB84900948DD1 /* BaseRxTableViewCell.swift */,
);
path = BaseRxTableViewCell;
sourceTree = "<group>";
};
52421F9124EBCF6E00948DD1 /* ViewModels */ = {
isa = PBXGroup;
children = (
52421F9224EBCF8600948DD1 /* TappableViewModel */,
);
path = ViewModels;
sourceTree = "<group>";
};
52421F9224EBCF8600948DD1 /* TappableViewModel */ = {
isa = PBXGroup;
children = (
52421F9524EBCFBB00948DD1 /* BaseTappableViewModel.swift */,
52421F9324EBCFAE00948DD1 /* VoidTappableViewModel.swift */,
);
path = TappableViewModel;
sourceTree = "<group>";
};
671461C41EB3396E00EAB194 /* Classes */ = {
isa = PBXGroup;
children = (
@ -882,6 +923,7 @@
6774527E2062566D0024EEEF /* DataLoading */,
671461D21EB3396E00EAB194 /* Services */,
671461D41EB3396E00EAB194 /* Views */,
52421F9124EBCF6E00948DD1 /* ViewModels */,
);
path = Classes;
sourceTree = "<group>";
@ -919,6 +961,8 @@
671461D41EB3396E00EAB194 /* Views */ = {
isa = PBXGroup;
children = (
52421F9024EAB84E00948DD1 /* BaseRxTableViewCell */,
52421F8B24EAB52E00948DD1 /* ContainerTableCell */,
72005A1A2266226800ECE090 /* CustomizableButton */,
677B06B6211873E7006C947D /* BasePlaceholderView */,
67DB77672108714A001CB56B /* CollectionViewWrapperView */,
@ -2412,6 +2456,7 @@
678D26A420692BFF00B05B93 /* TextFieldViewModelEvents.swift in Sources */,
671462801EB3396E00EAB194 /* DataRequest+Extensions.swift in Sources */,
67EB7FF8206175F700BDD9FB /* PaginationWrappable.swift in Sources */,
52421F9624EBCFBB00948DD1 /* BaseTappableViewModel.swift in Sources */,
67990AD6213EA6A50040D195 /* ContentLoadingViewModel+Extensions.swift in Sources */,
671463541EB3396E00EAB194 /* StaticViewHeightProtocol.swift in Sources */,
4CF65D1824DD6C080006B001 /* ButtonHolderView.swift in Sources */,
@ -2557,8 +2602,10 @@
411073AF23466B41002DD9B9 /* UIViewController+PresentFullScreen.swift in Sources */,
671462941EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */,
6741CEA920E2418B00FEC4D9 /* CollectionViewHolder.swift in Sources */,
52421F9424EBCFAE00948DD1 /* VoidTappableViewModel.swift in Sources */,
67745279206252020024EEEF /* DataLoadingState.swift in Sources */,
671463641EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
52421F8F24EAB84900948DD1 /* BaseRxTableViewCell.swift in Sources */,
67EB7FDA20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
671462481EB3396E00EAB194 /* FixedPageCursor.swift in Sources */,
671462C81EB3396E00EAB194 /* String+Localization.swift in Sources */,
@ -2606,6 +2653,7 @@
678D267920691D8200B05B93 /* DataModelFieldBinding.swift in Sources */,
72AECC71224A97F100D12E7C /* SearchResultsViewController.swift in Sources */,
673CF4342063E29B00C329F6 /* TextWithButtonPlaceholder.swift in Sources */,
52421F8D24EAB52E00948DD1 /* ContainerTableCell.swift in Sources */,
673CF4222063D90600C329F6 /* DisposeBagHolder.swift in Sources */,
67DB776D210871E8001CB56B /* BaseCollectionContentController.swift in Sources */,
82B4F8DB223903B800F6708C /* Block.swift in Sources */,

View File

@ -0,0 +1,44 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxCocoa
import RxSwift
open class BaseTappableViewModel<PayloadType> {
private let tapRelay = PublishRelay<PayloadType>()
public var tapDriver: Driver<PayloadType> {
tapRelay.asDriver(onErrorDriveWith: .empty())
}
public var tapObservable: Observable<PayloadType> {
tapRelay.asObservable()
}
public func bind(tapObservable: Observable<PayloadType>) -> Disposable {
tapObservable.bind(to: tapRelay)
}
public func tap(payload: PayloadType) {
tapRelay.accept(payload)
}
}

View File

@ -0,0 +1,27 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
open class VoidTappableViewModel: BaseTappableViewModel<Void> {
public func tap() {
tap(payload: ())
}
}

View File

@ -0,0 +1,73 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
open class BaseRxTableViewCell: UITableViewCell, InitializableView, DisposeBagHolder {
// MARK: - Properties
public var disposeBag = DisposeBag()
// MARK: - Initialization
override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
initializeView()
}
@available(*, unavailable)
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: - Override
override open func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
// MARK: - InitializableView
open func addViews() {
// overriding
}
open func bindViews() {
// overriding
}
open func configureLayout() {
// overriding
}
open func configureAppearance() {
selectionStyle = .none
}
open func localize() {
// overriding
}
}

View File

@ -0,0 +1,79 @@
//
// Copyright (c) 2020 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import RxSwift
import TableKit
open class ContainerTableCell<TView: UIView>: BaseRxTableViewCell, ConfigurableCell where TView: ConfigurableView {
// MARK: - Properties
private let wrappedView = TView()
open var shouldConfigureDefaultConstraints: Bool {
true
}
open var contentInsets: UIEdgeInsets {
.zero
}
open var contentViewBackgroundColor: UIColor {
.clear
}
// MARK: - ConfigurableCell
open func configure(with viewModel: TView.ViewModelType) {
disposeBag = DisposeBag()
wrappedView.configure(with: viewModel)
}
// MARK: - InitializableView
override open func addViews() {
super.addViews()
contentView.addSubview(wrappedView)
}
override open func configureLayout() {
super.configureLayout()
if shouldConfigureDefaultConstraints {
wrappedView.snp.makeConstraints {
$0.edges.equalToSuperview().inset(contentInsets)
}
} else {
configureCustomConstraints(forWrappedView: wrappedView)
}
}
override open func configureAppearance() {
super.configureAppearance()
contentView.backgroundColor = contentViewBackgroundColor
backgroundColor = contentViewBackgroundColor
}
open func configureCustomConstraints(forWrappedView view: TView) { }
}