Skip to content
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

Use UIListContentConfiguration for all the cells #37

Merged
merged 2 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Sample iOS app written the way I write iOS apps because I cannot share the app I
* Unit testing, including [testing view controllers for leaks](https://blog.kulman.sk/unit-testing-memory-leaks/)
* Creating a view controller in code when Storyboard cannot be used
* Using static UITableView cells in a typed way with enums
* Creating simple cells with UIListContentConfiguration
* Automated AppStore screenshots taking in multiple languages
* Adding custom reactive properties
* Basic Dark mode support
Expand Down
4 changes: 4 additions & 0 deletions Sources/iOSSampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
F3C71AB72B002A8A00EEAC8E /* SpecLeaks in Frameworks */ = {isa = PBXBuildFile; productRef = F3C71AB62B002A8A00EEAC8E /* SpecLeaks */; };
F3C71ABA2B002B2400EEAC8E /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = F3C71AB92B002B2400EEAC8E /* Nimble */; };
F3C71ABD2B002BB500EEAC8E /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = F3C71ABC2B002BB500EEAC8E /* Quick */; };
F3C71ABF2B00F26C00EEAC8E /* Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3C71ABE2B00F26C00EEAC8E /* Library.swift */; };
F3C7CFDD2423AF4C003A961E /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3C7CFDC2423AF4C003A961E /* URL+Extensions.swift */; };
F3C8DB3B214EA1B700C1A654 /* DataServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3C8DB3A214EA1B700C1A654 /* DataServiceMock.swift */; };
F3C8DB3D214EA1E700C1A654 /* SettingsServiceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3C8DB3C214EA1E700C1A654 /* SettingsServiceMock.swift */; };
Expand Down Expand Up @@ -165,6 +166,7 @@
F3C5F35B2961E45000257080 /* AboutCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutCell.swift; sourceTree = "<group>"; };
F3C5F35D2961EADD00257080 /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
F3C5F3622962C56300257080 /* FormFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldView.swift; sourceTree = "<group>"; };
F3C71ABE2B00F26C00EEAC8E /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = "<group>"; };
F3C7CFDC2423AF4C003A961E /* URL+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+Extensions.swift"; sourceTree = "<group>"; };
F3C8DB3A214EA1B700C1A654 /* DataServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataServiceMock.swift; sourceTree = "<group>"; };
F3C8DB3C214EA1E700C1A654 /* SettingsServiceMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsServiceMock.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -578,6 +580,7 @@
isa = PBXGroup;
children = (
F3C5F3592961E3BB00257080 /* AboutMenuItem.swift */,
F3C71ABE2B00F26C00EEAC8E /* Library.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -867,6 +870,7 @@
F3A812B81F83740E00A09AAB /* AppDelegate.swift in Sources */,
F38CD2371F83AEC70050056C /* UIScrollView+Extensions.swift in Sources */,
F3651D22203C1B0D0082A73A /* DataService.swift in Sources */,
F3C71ABF2B00F26C00EEAC8E /* Library.swift in Sources */,
F3C5F3542961BD8300257080 /* UIView+Layout.swift in Sources */,
F3C5F3632962C56300257080 /* FormFieldView.swift in Sources */,
F3C5F35E2961EADD00257080 /* UIEdgeInsets+Extensions.swift in Sources */,
Expand Down
19 changes: 9 additions & 10 deletions Sources/iOSSampleApp/Scenarios/About/Cells/AboutCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,16 @@ import Reusable
import UIKit

final class AboutCell: UITableViewCell, Reusable {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Properties

var model: AboutMenuItem?

// MARK: - Configuration

private func setup() {
textLabel?.font = UIFont.preferredFont(forTextStyle: .body)
override func updateConfiguration(using state: UICellConfigurationState) {
contentConfiguration = defaultContentConfiguration() &> {
$0.text = model?.title
}
}
}
16 changes: 15 additions & 1 deletion Sources/iOSSampleApp/Scenarios/About/Cells/LibraryCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,18 @@ import Foundation
import Reusable
import UIKit

final class LibraryCell: UITableViewCell, Reusable { }
final class LibraryCell: UITableViewCell, Reusable {

// MARK: - Properties

var model: Library?

// MARK: - Configuration

override func updateConfiguration(using state: UICellConfigurationState) {
contentConfiguration = UIListContentConfiguration.subtitleCell() &> {
$0.text = model?.title
$0.secondaryText = model?.license
}
}
}
14 changes: 14 additions & 0 deletions Sources/iOSSampleApp/Scenarios/About/Model/Library.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Library.swift
// iOSSampleApp
//
// Created by Igor Kulman on 12.11.2023.
// Copyright © 2023 Igor Kulman. All rights reserved.
//

import Foundation

struct Library {
let title: String
let license: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ final class AboutViewController: UITableViewController {
tableView.register(cellType: AboutCell.self)

Observable.just(AboutMenuItem.allCases).bind(to: tableView.rx.items(cellIdentifier: AboutCell.reuseIdentifier, cellType: AboutCell.self)) { _, element, cell in
cell.textLabel?.text = element.title
cell.model = element
}.disposed(by: disposeBag)

tableView.rx.modelSelected(AboutMenuItem.self).withUnretained(self).bind { owner, menuItem in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ final class LibrariesViewController: UITableViewController {
private func setupData() {
tableView.dataSource = nil
tableView.register(cellType: LibraryCell.self)
tableView.allowsSelection = false

viewModel.libraries.bind(to: tableView.rx.items(cellIdentifier: LibraryCell.reuseIdentifier, cellType: LibraryCell.self)) { _, element, cell in
let (name, licenseName) = element
cell.textLabel?.text = name
cell.detailTextLabel?.text = licenseName
cell.model = element
}.disposed(by: disposeBag)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
import Foundation
import RxSwift

typealias Library = (String, String)

final class LibrariesViewModel {

// MARK: - Properties
Expand All @@ -25,7 +23,7 @@ final class LibrariesViewModel {
libraries = Observable.just(array.map {
let title = $0["title"] as! String
let license = $0["license"] as! String
return (title, license)
return Library(title: title, license: license)
})
}
}
49 changes: 9 additions & 40 deletions Sources/iOSSampleApp/Scenarios/Feed/Cells/FeedCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,20 @@ import UIKit

final class FeedCell: UITableViewCell, Reusable {

// MARK: - UI

private lazy var titleLabel: UILabel = .init() &> {
$0.font = UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 17, weight: .semibold))
$0.adjustsFontForContentSizeCategory = true
}

private lazy var descriptionLabel: UILabel = .init() &> {
$0.font = UIFont.preferredFont(forTextStyle: .subheadline)
$0.numberOfLines = 3
}

// MARK: - Properties

var model: RssItem? {
didSet {
guard let model = model else {
return
}
var model: RssItem?

titleLabel.text = model.title
descriptionLabel.text = model.description?.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)
}
}
// MARK: - Configuration

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateConfiguration(using state: UICellConfigurationState) {
contentConfiguration = UIListContentConfiguration.subtitleCell() &> {
$0.text = model?.title
$0.secondaryText = model?.description?.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)

// MARK: - Setup

private func setup() {
preservesSuperviewLayoutMargins = true
contentView.preservesSuperviewLayoutMargins = true

let stackView: UIStackView = .init(arrangedSubviews: [titleLabel, descriptionLabel]) &> {
$0.axis = .vertical
$0.textProperties.font = UIFontMetrics.default.scaledFont(for: UIFont.systemFont(ofSize: 17, weight: .semibold))
$0.secondaryTextProperties.font = UIFont.preferredFont(forTextStyle: .subheadline)
$0.secondaryTextProperties.numberOfLines = 3
}

stackView.pin(to: self, guide: layoutMarginsGuide)
}
}