版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.01.19 星期六 |
前言
IGListKit
这个框架可能很多人没有听过,它其实就是一个数据驱动的UICollectionView
框架,用于构建快速灵活的列表。它由
1. IGListKit框架详细解析(一) —— 基本概览(一)
2. IGListKit框架详细解析(二) —— 基于IGListKit框架的更好的UICollectionViews简单示例(一)
3. IGListKit框架详细解析(三) —— 基于IGListKit框架的更好的UICollectionViews简单示例(二)
源码
1. Swift
首先看下工程组织结构
接下来就是代码了
1. AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = .black
let nav = UINavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil)
nav.pushViewController(FeedViewController(), animated: false)
window?.rootViewController = nav
window?.makeKeyAndVisible()
return true
}
}
2. DateSortable.swift
import Foundation
protocol DateSortable {
var date: Date { get }
}
3. JournalEntry.swift
import Foundation
class JournalEntry: NSObject, DateSortable {
let date: Date
let text: String
let user: User
init(date: Date, text: String, user: User) {
self.date = date
self.text = text
self.user = user
}
}
4. Message.swift
import UIKit
class Message: NSObject, DateSortable {
let date: Date
let text: String
let user: User
init(date: Date, text: String, user: User) {
self.date = date
self.text = text
self.user = user
}
}
5. SolFormatter.swift
import Foundation
struct SolFormatter {
let landingDate: Date
init(landingDate: Date = Date(timeIntervalSinceNow: -31725960)) {
self.landingDate = landingDate
}
func sols(fromDate date: Date) -> Int {
let martianDay: TimeInterval = 1477 * 60 // 24h37m
let seconds = date.timeIntervalSince(landingDate)
return lround(seconds / martianDay)
}
}
6. User.swift
import Foundation
class User: NSObject {
let id: Int
let name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
7. Weather.swift
import UIKit
enum WeatherCondition: String {
case cloudy = "Cloudy"
case sunny = "Sunny"
case partlyCloudy = "Partly Cloudy"
case dustStorm = "Dust Storm"
var emoji: String {
switch self {
case .cloudy: return "☁️"
case .sunny: return "☀️"
case .partlyCloudy: return "⛅️"
case .dustStorm: return "🌪"
}
}
}
class Weather: NSObject {
let temperature: Int
let high: Int
let low: Int
let date: Date
let sunrise: String
let sunset: String
let condition: WeatherCondition
init(
temperature: Int,
high: Int,
low: Int,
date: Date,
sunrise: String,
sunset: String,
condition: WeatherCondition
) {
self.temperature = temperature
self.high = high
self.low = low
self.date = date
self.sunrise = sunrise
self.sunset = sunset
self.condition = condition
}
}
8. NSObject+ListDiffable.swift
import Foundation
import IGListKit
// MARK: - ListDiffable
extension NSObject: ListDiffable {
public func diffIdentifier() -> NSObjectProtocol {
return self
}
public func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
return isEqual(object)
}
}
9. JournalSectionController.swift
import IGListKit
class JournalSectionController: ListSectionController {
var entry: JournalEntry!
let solFormatter = SolFormatter()
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
// MARK: - Data Provider
extension JournalSectionController {
override func numberOfItems() -> Int {
return 2
}
override func sizeForItem(at index: Int) -> CGSize {
guard
let context = collectionContext,
let entry = entry
else {
return .zero
}
let width = context.containerSize.width
if index == 0 {
return CGSize(width: width, height: 30)
} else {
return JournalEntryCell.cellSize(width: width, text: entry.text)
}
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
let cellClass: AnyClass = index == 0 ? JournalEntryDateCell.self : JournalEntryCell.self
let cell = collectionContext!.dequeueReusableCell(of: cellClass, for: self, at: index)
if let cell = cell as? JournalEntryDateCell {
cell.label.text = "SOL \(solFormatter.sols(fromDate: entry.date))"
} else if let cell = cell as? JournalEntryCell {
cell.label.text = entry.text
}
return cell
}
override func didUpdate(to object: Any) {
entry = object as? JournalEntry
}
}
10. MessageSectionController.swift
import IGListKit
class MessageSectionController: ListSectionController {
var message: Message!
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
// MARK: - Data Provider
extension MessageSectionController {
override func numberOfItems() -> Int {
return 1
}
override func sizeForItem(at index: Int) -> CGSize {
guard
let context = collectionContext,
let message = message
else {
return .zero
}
return MessageCell.cellSize(width: context.containerSize.width, text: message.text)
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext?.dequeueReusableCell(of: MessageCell.self, for: self, at: index) as! MessageCell
cell.messageLabel.text = message.text
cell.titleLabel.text = message.user.name.uppercased()
return cell
}
override func didUpdate(to object: Any) {
message = object as? Message
}
}
11. WeatherSectionController.swift
import IGListKit
class WeatherSectionController: ListSectionController {
var weather: Weather!
var expanded = false
override init() {
super.init()
inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
}
}
// MARK: - Data Provider
extension WeatherSectionController {
override func didUpdate(to object: Any) {
weather = object as? Weather
}
override func numberOfItems() -> Int {
return expanded ? 5 : 1
}
override func sizeForItem(at index: Int) -> CGSize {
guard let context = collectionContext else {
return .zero
}
let width = context.containerSize.width
if index == 0 {
return CGSize(width: width, height: 70)
} else {
return CGSize(width: width, height: 40)
}
}
override func cellForItem(at index: Int) -> UICollectionViewCell {
let cellClass: AnyClass = index == 0 ? WeatherSummaryCell.self : WeatherDetailCell.self
let cell = collectionContext!.dequeueReusableCell(of: cellClass, for: self, at: index)
if let cell = cell as? WeatherSummaryCell {
cell.setExpanded(expanded)
} else if let cell = cell as? WeatherDetailCell {
let title: String, detail: String
switch index {
case 1:
title = "SUNRISE"
detail = weather.sunrise
case 2:
title = "SUNSET"
detail = weather.sunset
case 3:
title = "HIGH"
detail = "\(weather.high) C"
case 4:
title = "LOW"
detail = "\(weather.low) C"
default:
title = "n/a"
detail = "n/a"
}
cell.titleLabel.text = title
cell.detailLabel.text = detail
}
return cell
}
override func didSelectItem(at index: Int) {
collectionContext?.performBatch(animated: true, updates: { batchContext in
self.expanded.toggle()
batchContext.reload(self)
}, completion: nil)
}
}
12. JournalLoader.swift
import Foundation
class JournalEntryLoader {
var entries: [JournalEntry] = []
func loadLatest() {
let user = User(id: 1, name: "Mark Watney")
let entries = [
JournalEntry(
date: Date(timeIntervalSinceNow: -1727283),
text: "Ok I think I have this potato thing figured out. I'm using some of the leftover fuel from the landing thruster and basically lighting it on fire. The hydrogen and oxygen combine to make water. If I throttle the reaction I can let this run all day and generate enough water in the air to hydrate my potatos.\n\nThough, I'm basically igniting jet fuel in my living room.",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -1382400),
text: "I blew up.\n\nMy potato hydration system was working perfectly, but I forgot to account for excess oxygen from the reaction. I ended up with 30% pure oxygen in the HAB. Where I'm making mini explosions. Oh did I mention I live here?\n\nI survived but the HAB is basically gone, along with all my potatos. The cold air instantly froze the ones I have, so there's that at least.",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -823200),
text: "I figured out how to communicate with NASA! Years ago we sent a small probe called Pathfinder to Mars to poke at the sand a bit. The little rover only lasted a couple months, but I found it! All I had to do was swap the batteries and its as good as new.\n\nWith all this in place I can send pictures to NASA, maybe Johansen can tell me how to hack this thing?",
user: user
),
JournalEntry(
date: Date(timeIntervalSinceNow: -259200),
text: "Alright, its time for me to leave the HAB and make the several-thousand kilometer trek to the next landing site. The MAV is already there, so I'm going to try to launch this thing and intercept with Hermes. Sounds crazy, right?\n\nBut it's the last chance I've got.",
user: user
)
]
self.entries = entries
}
}
13. Pathfinder.swift
import Foundation
protocol PathfinderDelegate: class {
func pathfinderDidUpdateMessages(pathfinder: Pathfinder)
}
private func delay(time: Double = 1, execute work: @escaping @convention(block) () -> Swift.Void) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
work()
}
}
private func lewisMessage(text: String, interval: TimeInterval = 0) -> Message {
let user = User(id: 2, name: "cpt.lewis")
return Message(date: Date(timeIntervalSinceNow: interval), text: text, user: user)
}
class Pathfinder {
weak var delegate: PathfinderDelegate?
var messages: [Message] = {
var arr: [Message] = []
arr.append(lewisMessage(text: "Mark, are you receiving me?", interval: -803200))
arr.append(lewisMessage(text: "I think I left behind some ABBA, might help with the drive 😜", interval: -259200))
return arr
}() {
didSet {
delegate?.pathfinderDidUpdateMessages(pathfinder: self)
}
}
func connect() {
delay(time: 2.3) {
self.messages.append(lewisMessage(text: "Liftoff in 3..."))
delay {
self.messages.append(lewisMessage(text: "2..."))
delay {
self.messages.append(lewisMessage(text: "1..."))
}
}
}
}
}
14. TextSize.swift
import UIKit
public struct TextSize {
private struct CacheEntry: Hashable, Equatable {
let text: String
let font: UIFont
let width: CGFloat
let insets: UIEdgeInsets
func hash(into hasher: inout Hasher) {
hasher.combine(text)
hasher.combine(width)
hasher.combine(insets.top)
hasher.combine(insets.left)
hasher.combine(insets.bottom)
hasher.combine(insets.right)
}
static func ==(lhs: TextSize.CacheEntry, rhs: TextSize.CacheEntry) -> Bool {
return lhs.width == rhs.width && lhs.insets == rhs.insets && lhs.text == rhs.text
}
}
private static var cache: [CacheEntry: CGRect] = [:] {
didSet {
assert(Thread.isMainThread)
}
}
public static func size(_ text: String, font: UIFont, width: CGFloat, insets: UIEdgeInsets = .zero) -> CGRect {
let key = CacheEntry(text: text, font: font, width: width, insets: insets)
if let hit = cache[key] {
return hit
}
let constrainedSize = CGSize(width: width - insets.left - insets.right, height: .greatestFiniteMagnitude)
let attributes = [NSAttributedString.Key.font: font]
let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
var bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes, context: nil)
bounds.size.width = width
bounds.size.height = ceil(bounds.height + insets.top + insets.bottom)
cache[key] = bounds
return bounds
}
}
15. Theme.swift
import UIKit
extension UIColor {
// https://github.com/yeahdongcn/UIColor-Hex-Swift/blob/master/HEXColor/UIColorExtension.swift
public convenience init(hex6: UInt32, alpha: CGFloat = 1) {
let divisor = CGFloat(255)
let red = CGFloat((hex6 & 0xFF0000) >> 16) / divisor
let green = CGFloat((hex6 & 0x00FF00) >> 8) / divisor
let blue = CGFloat( hex6 & 0x0000FF ) / divisor
self.init(red: red, green: green, blue: blue, alpha: alpha)
}
}
let CommonInsets = UIEdgeInsets(top: 8, left: 15, bottom: 8, right: 15)
func AppFont(size: CGFloat = 18) -> UIFont {
return UIFont(name: "OCRAStd", size: size)!
}
16. WxScanner.swift
import Foundation
class WxScanner {
let currentWeather = Weather(
temperature: 6,
high: 13,
low: -69,
date: Date(),
sunrise: "05:42",
sunset: "17:58",
condition: .dustStorm
)
}
17. ClassicFeedViewController.swift
import UIKit
class ClassicFeedViewController: UIViewController {
let loader = JournalEntryLoader()
let solFormatter = SolFormatter()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
view.backgroundColor = .black
view.alwaysBounceVertical = true
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(JournalEntryCell.self, forCellWithReuseIdentifier: "JournalEntryCell")
collectionView.register(JournalEntryDateCell.self, forCellWithReuseIdentifier: "JournalEntryDateCell")
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
loader.loadLatest()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
}
// MARK: - UICollectionViewDataSource
extension ClassicFeedViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return loader.entries.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier = indexPath.item == 0 ? "JournalEntryDateCell" : "JournalEntryCell"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
let entry = loader.entries[indexPath.section]
if let cell = cell as? JournalEntryDateCell {
cell.label.text = "SOL \(solFormatter.sols(fromDate: entry.date))"
} else if let cell = cell as? JournalEntryCell {
cell.label.text = entry.text
}
return cell
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ClassicFeedViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.bounds.width
if indexPath.item == 0 {
return CGSize(width: width, height: 30)
} else {
let entry = loader.entries[indexPath.section]
return JournalEntryCell.cellSize(width: width, text: entry.text)
}
}
}
18. FeedViewController.swift
import UIKit
import IGListKit
class FeedViewController: UIViewController {
let loader = JournalEntryLoader()
let collectionView: UICollectionView = {
let view = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
view.backgroundColor = .black
return view
}()
lazy var adapter: ListAdapter = {
return ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
}()
let pathfinder = Pathfinder()
let wxScanner = WxScanner()
override func viewDidLoad() {
super.viewDidLoad()
loader.loadLatest()
view.addSubview(collectionView)
adapter.collectionView = collectionView
adapter.dataSource = self
pathfinder.delegate = self
pathfinder.connect()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
}
// MARK: - ListAdapterDataSource
extension FeedViewController: ListAdapterDataSource {
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
var items: [ListDiffable] = [wxScanner.currentWeather]
items += loader.entries as [ListDiffable]
items += pathfinder.messages as [ListDiffable]
return items.sorted { (left: Any, right: Any) -> Bool in
guard let left = left as? DateSortable, let right = right as? DateSortable else {
return false
}
return left.date > right.date
}
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
if object is Message {
return MessageSectionController()
} else if object is Weather {
return WeatherSectionController()
} else {
return JournalSectionController()
}
}
func emptyView(for listAdapter: ListAdapter) -> UIView? {
return nil
}
}
// MARK: - PathfinderDelegate
extension FeedViewController: PathfinderDelegate {
func pathfinderDidUpdateMessages(pathfinder: Pathfinder) {
adapter.performUpdates(animated: true)
}
}
19. CustomNavigationBar.swift
import UIKit
class CustomNavigationBar: UINavigationBar {
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "MARSLINK"
label.font = AppFont()
label.textAlignment = .center
label.textColor = .white
return label
}()
let statusLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "RECEIVING"
label.font = AppFont(size: 13)
label.textAlignment = .center
label.textColor = UIColor(hex6: 0x42c84b)
label.sizeToFit()
return label
}()
let statusIndicator: CAShapeLayer = {
let layer = CAShapeLayer()
layer.strokeColor = UIColor.white.cgColor
layer.lineWidth = 1
layer.fillColor = UIColor.black.cgColor
let size: CGFloat = 8
let frame = CGRect(x: 0, y: 0, width: size, height: size)
layer.path = UIBezierPath(roundedRect: frame, cornerRadius: size / 2).cgPath
layer.frame = frame
return layer
}()
let highlightLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.fillColor = UIColor(hex6: 0x76879D).cgColor
return layer
}()
var statusOn = false
override init(frame: CGRect) {
super.init(frame: frame)
layer.addSublayer(highlightLayer)
layer.addSublayer(statusIndicator)
addSubview(titleLabel)
addSubview(statusLabel)
barTintColor = .black
updateStatus()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let titleWidth: CGFloat = 130
let borderHeight: CGFloat = 4
let path = UIBezierPath()
path.move(to: .zero)
path.addLine(to: CGPoint(x: titleWidth, y: 0))
path.addLine(to: CGPoint(x: titleWidth, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height - borderHeight))
path.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
path.addLine(to: CGPoint(x: 0, y: bounds.height))
path.close()
highlightLayer.path = path.cgPath
titleLabel.frame = CGRect(x: 0, y: 0, width: titleWidth, height: bounds.height)
statusLabel.frame = CGRect(
x: bounds.width - statusLabel.bounds.width - CommonInsets.right,
y: bounds.height - borderHeight - statusLabel.bounds.height - 6,
width: statusLabel.bounds.width,
height: statusLabel.bounds.height
)
statusIndicator.position = CGPoint(x: statusLabel.center.x - 50, y: statusLabel.center.y - 1)
}
func updateStatus() {
statusOn.toggle()
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
statusIndicator.fillColor = (statusOn ? UIColor.white : UIColor.black).cgColor
CATransaction.commit()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6) {
self.updateStatus()
}
}
}
20. JournalEntryCell.swift
import UIKit
class JournalEntryCell: UICollectionViewCell {
static let font = AppFont()
static let inset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15)
static func cellSize(width: CGFloat, text: String) -> CGSize {
return TextSize.size(text, font: JournalEntryCell.font, width: width, insets: JournalEntryCell.inset).size
}
let label: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.numberOfLines = 0
label.font = JournalEntryCell.font
label.textColor = .white
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(label)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
label.frame = bounds.inset(by: JournalEntryCell.inset)
}
}
21. JournalEntryDateCell.swift
import UIKit
class JournalEntryDateCell: UICollectionViewCell {
let label: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.font = AppFont(size: 14)
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(label)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let padding = CommonInsets
label.frame = bounds.inset(by: UIEdgeInsets(top: 0, left: padding.left, bottom: 0, right: padding.right))
}
}
22. MessageCell.swift
import UIKit
class MessageCell: UICollectionViewCell {
static let titleHeight: CGFloat = 30
static let font = AppFont()
static func cellSize(width: CGFloat, text: String) -> CGSize {
let labelBounds = TextSize.size(text, font: MessageCell.font, width: width, insets: CommonInsets)
return CGSize(width: width, height: labelBounds.height + MessageCell.titleHeight)
}
let messageLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.numberOfLines = 0
label.font = MessageCell.font
label.textColor = .white
return label
}()
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.font = AppFont(size: 14)
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()
let statusLabel: UILabel = {
let label = UILabel()
label.layer.borderColor = UIColor(hex6: 0x76879d).cgColor
label.layer.borderWidth = 1
label.backgroundColor = .clear
label.font = AppFont(size: 8)
label.textColor = UIColor(hex6: 0x76879d)
label.textAlignment = .center
label.text = "NEW MESSAGE"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
contentView.addSubview(messageLabel)
contentView.addSubview(titleLabel)
contentView.addSubview(statusLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.frame = CGRect(x: CommonInsets.left, y: 0, width: bounds.width - CommonInsets.left - CommonInsets.right, height: MessageCell.titleHeight)
statusLabel.frame = CGRect(x: bounds.width - 80, y: 4, width: 70, height: 18)
let messageFrame = CGRect(x: 0, y: titleLabel.frame.maxY, width: bounds.width, height: bounds.height - MessageCell.titleHeight)
messageLabel.frame = messageFrame.inset(by: CommonInsets)
}
}
23. WeatherDetailCell.swift
import UIKit
class WeatherDetailCell: UICollectionViewCell {
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.font = AppFont()
label.textColor = UIColor(hex6: 0x42c84b)
return label
}()
let detailLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.font = AppFont()
label.textColor = UIColor(hex6: 0x42c84b)
label.textAlignment = .right
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(titleLabel)
contentView.addSubview(detailLabel)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let insetBounds = bounds.inset(by: CommonInsets)
titleLabel.frame = insetBounds
detailLabel.frame = insetBounds
}
}
24. WeatherSummaryCell.swift
import UIKit
class WeatherSummaryCell: UICollectionViewCell {
private let expandLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.font = AppFont(size: 30)
label.textColor = UIColor(hex6: 0x44758b)
label.textAlignment = .center
label.text = ">>"
label.sizeToFit()
return label
}()
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.numberOfLines = 0
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.paragraphSpacing = 4
let subtitleAttributes = [
NSAttributedString.Key.font: AppFont(size: 14),
NSAttributedString.Key.foregroundColor: UIColor(hex6: 0x42c84b),
NSAttributedString.Key.paragraphStyle: paragraphStyle
]
let titleAttributes = [
NSAttributedString.Key.font: AppFont(size: 24),
NSAttributedString.Key.foregroundColor: UIColor.white
]
let attributedText = NSMutableAttributedString(string: "LATEST\n", attributes: subtitleAttributes)
attributedText.append(NSAttributedString(string: "WEATHER", attributes: titleAttributes))
label.attributedText = attributedText
label.sizeToFit()
return label
}()
func setExpanded(_ expanded: Bool) {
expandLabel.transform = expanded ? CGAffineTransform(rotationAngle: CGFloat.pi / 2) : .identity
}
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(expandLabel)
contentView.addSubview(titleLabel)
contentView.backgroundColor = UIColor(hex6: 0x0c1f3f)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
let insets = CommonInsets
titleLabel.frame = CGRect(x: insets.left, y: 0, width: titleLabel.bounds.width, height: bounds.height)
expandLabel.center = CGPoint(x: bounds.width - expandLabel.bounds.width / 2 - insets.right, y: bounds.height / 2)
}
}
后记
本篇主要简单介绍了基于IGListKit框架的更好的UICollectionViews简单示例,感兴趣的给个赞或者关注~~~
网友评论