版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.04.28 星期二 |
前言
iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
1. UIKit框架(一) —— UIKit动力学和移动效果(一)
2. UIKit框架(二) —— UIKit动力学和移动效果(二)
3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)
6. UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)
7. UIKit框架(七) —— 动态尺寸UITableViewCell的实现(一)
8. UIKit框架(八) —— 动态尺寸UITableViewCell的实现(二)
9. UIKit框架(九) —— UICollectionView的数据异步预加载(一)
10. UIKit框架(十) —— UICollectionView的数据异步预加载(二)
11. UIKit框架(十一) —— UICollectionView的重用、选择和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用、选择和重排序(二)
13. UIKit框架(十三) —— 如何创建自己的侧滑式面板导航(一)
14. UIKit框架(十四) —— 如何创建自己的侧滑式面板导航(二)
15. UIKit框架(十五) —— 基于自定义UICollectionViewLayout布局的简单示例(一)
16. UIKit框架(十六) —— 基于自定义UICollectionViewLayout布局的简单示例(二)
17. UIKit框架(十七) —— 基于自定义UICollectionViewLayout布局的简单示例(三)
18. UIKit框架(十八) —— 基于CALayer属性的一种3D边栏动画的实现(一)
19. UIKit框架(十九) —— 基于CALayer属性的一种3D边栏动画的实现(二)
20. UIKit框架(二十) —— 基于UILabel跑马灯类似效果的实现(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定义viewController的转场和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定义viewController的转场和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在两个APP间的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定义布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定义布局 (二)
28. UIKit框架(二十八) —— 一个UISplitViewController的简单实用示例 (一)
29. UIKit框架(二十九) —— 一个UISplitViewController的简单实用示例 (二)
30. UIKit框架(三十) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(一)
31. UIKit框架(三十一) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的简单示例(二)
32. UIKit框架(三十二) —— 替换Peek and Pop交互的基于iOS13的Context Menus(一)
33. UIKit框架(三十三) —— 替换Peek and Pop交互的基于iOS13的Context Menus(二)
34. UIKit框架(三十四) —— Accessibility的使用(一)
35. UIKit框架(三十五) —— Accessibility的使用(二)
36. UIKit框架(三十六) —— UICollectionView UICollectionViewDiffableDataSource的使用(一)
源码
1. Swift
首先看下工程组织结构
下面就是源码了
1. AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
}
2. SceneDelegate.swift
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
}
3. VideosViewController.swift
import UIKit
import SafariServices
class VideosViewController: UICollectionViewController {
// MARK: - Properties
private var sections = Section.allSections
private lazy var dataSource = makeDataSource()
private var searchController = UISearchController(searchResultsController: nil)
// MARK: - Value Types
typealias DataSource = UICollectionViewDiffableDataSource
typealias Snapshot = NSDiffableDataSourceSnapshot
// MARK: - Life Cycles
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
configureSearchController()
configureLayout()
applySnapshot(animatingDifferences: false)
}
// MARK: - Functions
func makeDataSource() -> DataSource {
// 1
let dataSource = DataSource(
collectionView: collectionView,
cellProvider: { (collectionView, indexPath, video) ->
UICollectionViewCell? in
// 2
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "VideoCollectionViewCell",
for: indexPath) as? VideoCollectionViewCell
cell?.video = video
return cell
})
dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
guard kind == UICollectionView.elementKindSectionHeader else {
return nil
}
let section = self.dataSource.snapshot()
.sectionIdentifiers[indexPath.section]
let view = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: SectionHeaderReusableView.reuseIdentifier,
for: indexPath) as? SectionHeaderReusableView
view?.titleLabel.text = section.title
return view
}
return dataSource
}
// 1
func applySnapshot(animatingDifferences: Bool = true) {
// 2
var snapshot = Snapshot()
// 3
snapshot.appendSections(sections)
// 4
sections.forEach { section in
snapshot.appendItems(section.videos, toSection: section)
}
// 5
dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}
}
// MARK: - UICollectionViewDataSource Implementation
extension VideosViewController {
override func collectionView(
_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath
) {
guard let video = dataSource.itemIdentifier(for: indexPath) else {
return
}
guard let link = video.link else {
print("Invalid link")
return
}
let safariViewController = SFSafariViewController(url: link)
present(safariViewController, animated: true, completion: nil)
}
}
// MARK: - UISearchResultsUpdating Delegate
extension VideosViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
sections = filteredSections(for: searchController.searchBar.text)
applySnapshot()
}
func filteredSections(for queryOrNil: String?) -> [Section] {
let sections = Section.allSections
guard
let query = queryOrNil,
!query.isEmpty
else {
return sections
}
return sections.filter { section in
var matches = section.title.lowercased().contains(query.lowercased())
for video in section.videos {
if video.title.lowercased().contains(query.lowercased()) {
matches = true
break
}
}
return matches
}
}
func configureSearchController() {
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Videos"
navigationItem.searchController = searchController
definesPresentationContext = true
}
}
// MARK: - Layout Handling
extension VideosViewController {
private func configureLayout() {
collectionView.register(
SectionHeaderReusableView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: SectionHeaderReusableView.reuseIdentifier
)
collectionView.collectionViewLayout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
let isPhone = layoutEnvironment.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiom.phone
let size = NSCollectionLayoutSize(
widthDimension: NSCollectionLayoutDimension.fractionalWidth(1),
heightDimension: NSCollectionLayoutDimension.absolute(isPhone ? 280 : 250)
)
let itemCount = isPhone ? 1 : 3
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitem: item, count: itemCount)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
section.interGroupSpacing = 10
// Supplementary header view setup
let headerFooterSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(20)
)
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerFooterSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
section.boundarySupplementaryItems = [sectionHeader]
return section
})
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
self.collectionView.collectionViewLayout.invalidateLayout()
}, completion: nil)
}
}
// MARK: - SFSafariViewControllerDelegate Implementation
extension VideosViewController: SFSafariViewControllerDelegate {
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
4. VideoCollectionViewCell.swift
import UIKit
class VideoCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var thumbnailView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
var video: Video? {
didSet {
thumbnailView.image = video?.thumbnail
titleLabel.text = video?.title
subtitleLabel.text = "\(video?.lessonCount ?? 0) lessons"
}
}
}
5. Video.swift
import UIKit
class Video: Hashable {
var id = UUID()
var title: String
var thumbnail: UIImage?
var lessonCount: Int
var link: URL?
init(title: String, thumbnail: UIImage? = nil, lessonCount: Int, link: URL?) {
self.title = title
self.thumbnail = thumbnail
self.lessonCount = lessonCount
self.link = link
}
// 1
func hash(into hasher: inout Hasher) {
// 2
hasher.combine(id)
}
// 3
static func == (lhs: Video, rhs: Video) -> Bool {
lhs.id == rhs.id
}
}
6. Section.swift
import UIKit
// 1
class Section: Hashable {
var id = UUID()
// 2
var title: String
// 3
var videos: [Video]
init(title: String, videos: [Video]) {
self.title = title
self.videos = videos
}
// 4
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Section, rhs: Section) -> Bool {
lhs.id == rhs.id
}
}
extension Section {
static var allSections: [Section] = [
Section(title: "SwiftUI", videos: [
Video(
title: "SwiftUI",
thumbnail: UIImage(named: "swiftui"),
lessonCount: 37,
link: URL(string: "https://www.raywenderlich.com/4001741-swiftui")
)
]),
Section(title: "UIKit", videos: [
Video(
title: "Demystifying Views in iOS",
thumbnail: UIImage(named: "views"),
lessonCount: 26,
link:
URL(string:
"https://www.raywenderlich.com/4518-demystifying-views-in-ios")
),
Video(
title: "Reproducing Popular iOS Controls",
thumbnail: UIImage(named: "controls"),
lessonCount: 31,
link: URL(string: """
https://www.raywenderlich.com/5298-reproducing
-popular-ios-controls
""")
)
]),
Section(title: "Frameworks", videos: [
Video(
title: "Fastlane for iOS",
thumbnail: UIImage(named: "fastlane"),
lessonCount: 44,
link: URL(string:
"https://www.raywenderlich.com/1259223-fastlane-for-ios")
),
Video(
title: "Beginning RxSwift",
thumbnail: UIImage(named: "rxswift"),
lessonCount: 39,
link: URL(string:
"https://www.raywenderlich.com/4743-beginning-rxswift")
)
]),
Section(title: "Miscellaneous", videos: [
Video(
title: "Data Structures & Algorithms in Swift",
thumbnail: UIImage(named: "datastructures"),
lessonCount: 29,
link: URL(string: """
https://www.raywenderlich.com/977854-data-structures
-algorithms-in-swift
""")
),
Video(
title: "Beginning ARKit",
thumbnail: UIImage(named: "arkit"),
lessonCount: 46,
link: URL(string:
"https://www.raywenderlich.com/737368-beginning-arkit")
),
Video(
title: "Machine Learning in iOS",
thumbnail: UIImage(named: "machinelearning"),
lessonCount: 15,
link: URL(string: """
https://www.raywenderlich.com/1320561-machine-learning-in-ios
""")
),
Video(
title: "Push Notifications",
thumbnail: UIImage(named: "notifications"),
lessonCount: 33,
link: URL(string:
"https://www.raywenderlich.com/1258151-push-notifications")
),
])
]
}
7. SectionHeaderReusableView.swift
import UIKit
class SectionHeaderReusableView: UICollectionReusableView {
static var reuseIdentifier: String {
return String(describing: SectionHeaderReusableView.self)
}
lazy var titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(
ofSize: UIFont.preferredFont(forTextStyle: .title1).pointSize,
weight: .bold)
label.adjustsFontForContentSizeCategory = true
label.textColor = .label
label.textAlignment = .left
label.numberOfLines = 1
label.setContentCompressionResistancePriority(
.defaultHigh, for: .horizontal)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemBackground
addSubview(titleLabel)
if UIDevice.current.userInterfaceIdiom == .pad {
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(
equalTo: leadingAnchor,
constant: 5),
titleLabel.trailingAnchor.constraint(
lessThanOrEqualTo: trailingAnchor,
constant: -5)])
} else {
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(
equalTo: readableContentGuide.leadingAnchor),
titleLabel.trailingAnchor.constraint(
lessThanOrEqualTo: readableContentGuide.trailingAnchor)
])
}
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(
equalTo: topAnchor,
constant: 10),
titleLabel.bottomAnchor.constraint(
equalTo: bottomAnchor,
constant: -10)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
8. UIView+IBInspectable.swift
import UIKit
extension UIView {
@IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
@IBInspectable
var shadowOpacity: Float {
get {
return layer.shadowOpacity
}
set {
layer.shadowOpacity = newValue
}
}
@IBInspectable
var shadowOffset: CGSize {
get {
return layer.shadowOffset
}
set {
layer.shadowOffset = newValue
}
}
@IBInspectable
var shadowColor: CGColor? {
get {
return layer.shadowColor
}
set {
layer.shadowColor = newValue
}
}
@IBInspectable
var shadowRadius: CGFloat {
get {
return layer.shadowRadius
}
set {
layer.shadowRadius = newValue
}
}
}
后记
本篇主要讲述了
UICollectionView UICollectionViewDiffableDataSource
的使用,感兴趣的给个赞或者关注~~~