Skip to content

Commit

Permalink
Update iOS example
Browse files Browse the repository at this point in the history
  • Loading branch information
ra1028 committed Apr 30, 2019
1 parent ca5a624 commit 73e3522
Show file tree
Hide file tree
Showing 28 changed files with 854 additions and 507 deletions.
140 changes: 122 additions & 18 deletions Examples/Example-iOS/Example-iOS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions Examples/Example-iOS/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,25 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
configureUIAppearance()

let window = UIWindow()
let navigationController = UINavigationController(rootViewController: HomeViewController())
window.rootViewController = navigationController
window.makeKeyAndVisible()
self.window = window
return true
}

func configureUIAppearance() {
let appearance = UINavigationBar.appearance()
let titleTextAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black
]

appearance.tintColor = .darkText
appearance.prefersLargeTitles = true
appearance.titleTextAttributes = titleTextAttributes
appearance.largeTitleTextAttributes = titleTextAttributes
}
}
26 changes: 26 additions & 0 deletions Examples/Example-iOS/Sources/Common/NibLoadable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import UIKit

protocol NibLoadable: class {
static var nibName: String { get }
static var nibBundle: Bundle? { get }
}

extension NibLoadable {
static var nib: UINib {
return UINib(nibName: nibName, bundle: nibBundle)
}

static var nibName: String {
return String(describing: self)
}

static var nibBundle: Bundle? {
return Bundle(for: self)
}
}

extension NibLoadable where Self: UIView {
static func loadFromNib() -> Self {
return nib.instantiate(withOwner: nil, options: nil).first as! Self
}
}
11 changes: 11 additions & 0 deletions Examples/Example-iOS/Sources/Common/Reusable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
protocol Reusable: class {
static var reuseIdentifier: String { get }
}

extension Reusable {
static var reuseIdentifier: String {
return String(reflecting: self)
}
}

typealias NibReusable = NibLoadable & Reusable
65 changes: 65 additions & 0 deletions Examples/Example-iOS/Sources/Common/ReusableViewExtensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import UIKit

extension UITableView {
func register<T: UITableViewCell>(cellType: T.Type) where T: NibReusable {
register(T.nib, forCellReuseIdentifier: T.reuseIdentifier)
}

func register<T: UITableViewCell>(cellType: T.Type) where T: Reusable {
register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

func register<T: UITableViewHeaderFooterView>(viewType: T.Type) where T: NibReusable {
register(T.nib, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
}

func register<T: UITableViewHeaderFooterView>(viewType: T.Type) where T: Reusable {
register(T.self, forHeaderFooterViewReuseIdentifier: T.reuseIdentifier)
}

func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: Reusable {
guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError()
}
return cell
}

func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(viewType: T.Type = T.self) -> T where T: Reusable {
guard let view = dequeueReusableHeaderFooterView(withIdentifier: T.reuseIdentifier) as? T else {
fatalError()
}
return view
}
}

extension UICollectionView {
func register<T: UICollectionViewCell>(cellType: T.Type) where T: NibReusable {
register(T.nib, forCellWithReuseIdentifier: T.reuseIdentifier)
}

func register<T: UICollectionViewCell>(cellType: T.Type) where T: Reusable {
register(T.self, forCellWithReuseIdentifier: T.reuseIdentifier)
}

func register<T: UICollectionReusableView>(viewType: T.Type, forSupplementaryViewOfKind kind: String) where T: NibReusable {
register(T.nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: T.reuseIdentifier)
}

func register<T: UICollectionReusableView>(viewType: T.Type, forSupplementaryViewOfKind kind: String) where T: Reusable {
register(T.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: T.reuseIdentifier)
}

func dequeueReusableCell<T: UICollectionViewCell>(for indexPath: IndexPath, cellType: T.Type = T.self) -> T where T: Reusable {
guard let cell = dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
fatalError()
}
return cell
}

func dequeueReusableSupplementaryView<T: UICollectionReusableView>(ofKind kind: String, for indexPath: IndexPath, viewType: T.Type = T.self) -> T where T: Reusable {
guard let view = dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError()
}
return view
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import UIKit
import DifferenceKit

extension String: Differentiable {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import UIKit

final class HeaderFooterPlainCell: UITableViewCell, Reusable {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import UIKit

final class HeaderFooterMoreView: UITableViewHeaderFooterView, NibReusable {
var onMorePressed: (() -> Void)?

@IBAction func handleMorePressed() {
onMorePressed?()
}
}
39 changes: 39 additions & 0 deletions Examples/Example-iOS/Sources/HeaderFooter/HeaderFooterMoreView.xib
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="9Un-o1-tzh" customClass="HeaderFooterMoreView" customModule="Example_iOS" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="l5o-Rr-49h">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/>
<state key="normal" title="More"/>
<connections>
<action selector="handleMorePressed" destination="-2" eventType="touchUpInside" id="zzb-Pr-W6j"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="l5o-Rr-49h" firstAttribute="top" secondItem="9Un-o1-tzh" secondAttribute="top" id="6LZ-tU-yN1"/>
<constraint firstAttribute="bottom" secondItem="l5o-Rr-49h" secondAttribute="bottom" id="LQK-iA-9FV"/>
<constraint firstAttribute="trailing" secondItem="l5o-Rr-49h" secondAttribute="trailing" id="amX-Pz-M4V"/>
<constraint firstItem="l5o-Rr-49h" firstAttribute="leading" secondItem="9Un-o1-tzh" secondAttribute="leading" id="exN-an-G2q"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="0jJ-Kp-0ak"/>
<point key="canvasLocation" x="-42" y="-30"/>
</view>
</objects>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import DifferenceKit

struct HeaderFooterSectionModel: Differentiable, Equatable {
var id: Int
var hasFooter: Bool

var differenceIdentifier: Int {
return id
}

var headerTitle: String {
return "Section \(id)"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import UIKit
import DifferenceKit

final class HeaderFooterViewController: UITableViewController {
typealias Section = ArraySection<HeaderFooterSectionModel, String>

private var data = [Section]()

private var dataInput: [Section] {
get { return data }
set {
let changeset = StagedChangeset(source: data, target: newValue)
tableView.reload(using: changeset, with: .fade) { data in
self.data = data
}
}
}

private let allTexts = (0x0041...0x005A).compactMap { UnicodeScalar($0).map(String.init) }

@objc private func refresh() {
let model = HeaderFooterSectionModel(id: 0, hasFooter: true)
let section = Section(model: model, elements: allTexts.prefix(7))
dataInput = [section]
}

private func showMore(in sectionIndex: Int) {
var section = dataInput[sectionIndex]
let texts = allTexts.dropFirst(section.elements.count).prefix(7)
section.elements.append(contentsOf: texts)
section.model.hasFooter = section.elements.count < allTexts.count
dataInput[sectionIndex] = section

let lastIndex = section.elements.index(before: section.elements.endIndex)
let lastIndexPath = IndexPath(row: lastIndex, section: sectionIndex)
tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: true)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
refresh()
}

override func viewDidLoad() {
super.viewDidLoad()

title = "Header Footer"
tableView.allowsSelection = false

tableView.register(cellType: HeaderFooterPlainCell.self)
tableView.register(viewType: HeaderFooterMoreView.self)

navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .refresh, target: self, action: #selector(refresh))
}
}

extension HeaderFooterViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].elements.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: HeaderFooterPlainCell = tableView.dequeueReusableCell(for: indexPath)
cell.textLabel?.text = data[indexPath.section].elements[indexPath.row]
return cell
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return data[section].model.headerTitle
}

override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard data[section].model.hasFooter else { return nil }

let view: HeaderFooterMoreView = tableView.dequeueReusableHeaderFooterView()
view.onMorePressed = { [weak self] in
self?.showMore(in: section)
}

return view
}

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return data[section].model.hasFooter ? 44 : 0
}
}
Loading

0 comments on commit 73e3522

Please sign in to comment.