版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2019.08.21 星期三 |
前言
GameplayKit框架井赌,構建和組織你的游戲邏輯谤逼。 整合常見的游戲行為,如隨機數(shù)生成仇穗,人工智能流部,尋路和代理行為。接下來幾篇我們就一起看一下這個框架纹坐。感興趣的看下面幾篇文章枝冀。
1. GameplayKit框架詳細解析(一) —— 基本概覽(一)
2. GameplayKit框架詳細解析(二) —— GameplayKit的實用狀態(tài)機(一)
開始
1. Swift
首先看下工程組織結(jié)構
下面就是看代碼了
1. Coordinator.swift
protocol Coordinator {
func start()
}
2. ApplicationCoordinator.swift
import UIKit
class ApplicationCoordinator: Coordinator {
let kanjiStorage: KanjiStorage
let window: UIWindow
let rootViewController: UINavigationController
let stateMachine: KanjiStateMachine
init(window: UIWindow) {
self.window = window
kanjiStorage = KanjiStorage()
rootViewController = UINavigationController()
rootViewController.navigationBar.prefersLargeTitles = true
stateMachine = KanjiStateMachine(presenter: rootViewController,
kanjiStorage: kanjiStorage,
states: [AllState(), DetailState(), ListState()])
}
func start() {
stateMachine.enter(AllState.self)
window.rootViewController = rootViewController
window.makeKeyAndVisible()
subscribeToNotifications()
}
func subscribeToNotifications() {
NotificationCenter.default.addObserver(
self, selector: #selector(receivedAllKanjiNotification),
name: Notifications.AllKanji, object: nil)
NotificationCenter.default.addObserver(
self, selector: #selector(receivedKanjiDetailNotification),
name: Notifications.KanjiDetail, object: nil)
NotificationCenter.default.addObserver(
self, selector: #selector(receivedKanjiListNotification),
name: Notifications.KanjiList, object: nil)
}
@objc func receivedAllKanjiNotification() {
stateMachine.enter(AllState.self)
}
@objc func receivedKanjiDetailNotification(notification: NSNotification) {
// 1
guard
let kanji = notification.object as? Kanji,
// 2
let detailState = stateMachine.state(forClass: DetailState.self)
else {
return
}
// 3
detailState.kanji = kanji
// 4
stateMachine.enter(DetailState.self)
}
@objc func receivedKanjiListNotification(notification: NSNotification) {
// 1
guard
let word = notification.object as? String,
let listState = stateMachine.state(forClass: ListState.self)
else {
return
}
// 2
listState.word = word
// 3
stateMachine.enter(ListState.self)
}
}
3. KanjiListCoordinator.swift
import UIKit
class KanjiListCoordinator: Coordinator {
private let presenter: UINavigationController
private let kanjiList: [Kanji]
private let title: String
private var kanjiDetailCoordinator: KanjiDetailCoordinator?
private let kanjiStorage: KanjiStorage
init(presenter: UINavigationController,
kanjiStorage: KanjiStorage,
list: [Kanji],
title: String) {
self.presenter = presenter
self.kanjiStorage = kanjiStorage
self.kanjiList = list
self.title = title
}
func start() {
let listViewController = KanjiListViewController(kanjiList: kanjiList, title: title)
listViewController.delegate = self
presenter.pushViewController(listViewController, animated: true)
}
}
// MARK: - KanjiListViewControllerDelegate
extension KanjiListCoordinator: KanjiListViewControllerDelegate {
func kanjiListViewController(_ kanjiListViewController: KanjiListViewController,
didSelectKanji selectedKanji: Kanji) {
NotificationCenter.default.post(name: Notifications.KanjiDetail, object: selectedKanji)
}
}
4. KanjiDetailCoordinator.swift
import UIKit
class KanjiDetailCoordinator: Coordinator {
private let presenter: UINavigationController
private var kanjiListCoordinator: KanjiListCoordinator?
private let kanjiStorage: KanjiStorage
private let kanji: Kanji
init(presenter: UINavigationController, kanji: Kanji, kanjiStorage: KanjiStorage) {
self.kanji = kanji
self.presenter = presenter
self.kanjiStorage = kanjiStorage
}
func start() {
let kanjiDetailViewController = KanjiDetailViewController(kanji: kanji)
kanjiDetailViewController.delegate = self
presenter.pushViewController(kanjiDetailViewController, animated: true)
}
}
// MARK: - KanjiDetailViewControllerDelegate
extension KanjiDetailCoordinator: KanjiDetailViewControllerDelegate {
func kanjiDetailViewController(_ kanjiDetailViewController: KanjiDetailViewController,
didSelectWord word: String) {
NotificationCenter.default.post(name: Notifications.KanjiList, object: word)
}
}
5. AppDelegate.swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private var applicationCoordinator: ApplicationCoordinator?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
let applicationCoordinator = ApplicationCoordinator(window: window)
self.window = window
self.applicationCoordinator = applicationCoordinator
applicationCoordinator.start()
return true
}
}
6. Notifications.swift
import Foundation
enum Notifications {
static let AllKanji = NSNotification.Name(rawValue: "AllKanjiNotification")
static let KanjiDetail = NSNotification.Name(rawValue: "KanjiDetailNotification")
static let KanjiList = NSNotification.Name(rawValue: "KanjiListNotification")
}
7. KanjiStorage.swift
import Foundation
struct KanjiCache {
let kanjiArray: [Kanji]
let kanjiDictionary: [String: Kanji]
}
// Provides kanji data from JSON
class KanjiStorage {
static let kanjiURL = Bundle.main.url(forResource: "kanji", withExtension: "json")
private let allKanjiFromJSON: KanjiCache
init() {
// Parse json and store its data
guard
let kanjiURL = KanjiStorage.kanjiURL,
let data = try? Data(contentsOf: kanjiURL),
let allKanjis = try? JSONDecoder().decode([Kanji].self, from: data)
else {
preconditionFailure("Could not read kanji from kanji.json")
}
let kanjiDictionary = allKanjis.reduce([:]) { (dictionary, kanji) -> [String: Kanji] in
var dictionary = dictionary
dictionary[kanji.character] = kanji
return dictionary
}
// Save new cache
allKanjiFromJSON = KanjiCache(kanjiArray: allKanjis,
kanjiDictionary: kanjiDictionary)
}
func allKanji() -> [Kanji] {
return allKanjiFromJSON.kanjiArray
}
func kanjiForWord(_ word: String) -> [Kanji] {
let kanjiInWord: [Kanji] = word.compactMap { (character) -> Kanji? in
let kanjiForCharacter = allKanjiFromJSON.kanjiDictionary["\(character)"]
return kanjiForCharacter
}
return kanjiInWord
}
}
8. Kanji.swift
import Foundation
struct WordExample: Codable {
let word: String
let meaning: String
}
struct Kanji: Codable {
let character: String
let meaning: String
let examples: [WordExample]
}
9. KanjiViewController.swift
import UIKit
class KanjiViewController: UIViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
let allButton = UIBarButtonItem(title: "All",
style: .plain,
target: self,
action: #selector(allTapped))
navigationItem.rightBarButtonItem = allButton
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func allTapped() {
NotificationCenter.default.post(name: Notifications.AllKanji, object: nil)
}
}
10. KanjiDetailViewController.swift
import Foundation
import UIKit
protocol KanjiDetailViewControllerDelegate: class {
func kanjiDetailViewController(_ kanjiDetailViewController: KanjiDetailViewController,
didSelectWord word: String)
}
class KanjiDetailViewController: KanjiViewController {
let selectedKanji: Kanji
var delegate: KanjiDetailViewControllerDelegate?
@IBOutlet weak var detailTableView: UITableView? {
didSet {
guard let detailTableView = detailTableView else { return }
detailTableView.dataSource = self
detailTableView.delegate = self
// Word cell
let wordCellNib = UINib(nibName: "WordExampleTableViewCell", bundle: nil)
detailTableView.register(wordCellNib, forCellReuseIdentifier: "WordExampleTableViewCell")
// Detail cell
let detailCellNib = UINib(nibName: "KanjiDataTableViewCell", bundle: nil)
detailTableView.register(detailCellNib, forCellReuseIdentifier: "KanjiDataTableViewCell")
}
}
init(kanji: Kanji) {
selectedKanji = kanji
super.init(nibName: nil, bundle: nil)
title = "Kanji details"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - UITableViewDataSource
extension KanjiDetailViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 1: return "Words"
default: return nil
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: return 1
case 1: return selectedKanji.examples.count
default: return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "KanjiDataTableViewCell", for: indexPath)
(cell as? KanjiDataTableViewCell)?.setupCell(data: selectedKanji)
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "WordExampleTableViewCell", for: indexPath)
let word = selectedKanji.examples[indexPath.row]
(cell as? WordExampleTableViewCell)?.setupCell(data: word)
return cell
default:
fatalError()
}
}
}
// MARK: - UITableViewDelegate
extension KanjiDetailViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
defer {
tableView.deselectRow(at: indexPath, animated: true)
}
guard indexPath.section == 1 else {
return
}
let word = selectedKanji.examples[indexPath.row].word
delegate?.kanjiDetailViewController(self, didSelectWord: word)
}
}
11. KanjiListViewController.swift
import UIKit
protocol KanjiListViewControllerDelegate: class {
func kanjiListViewController(_ kanjiListViewController: KanjiListViewController,
didSelectKanji selectedKanji: Kanji)
}
class KanjiListViewController: KanjiViewController {
let kanjiList: [Kanji]
var delegate: KanjiListViewControllerDelegate?
@IBOutlet weak var kanjiListTableView: UITableView! {
didSet {
kanjiListTableView?.dataSource = self
kanjiListTableView?.delegate = self
}
}
init(kanjiList: [Kanji], title: String) {
self.kanjiList = kanjiList
super.init(nibName: nil, bundle: nil)
self.title = title
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension KanjiListViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return kanjiList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if let dequeuedCell = tableView.dequeueReusableCell(withIdentifier: "ListItem") {
cell = dequeuedCell
} else {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "ListItem")
}
let kanji = kanjiList[indexPath.row]
cell.textLabel?.text = kanji.character
cell.detailTextLabel?.text = kanji.meaning
cell.accessoryType = .disclosureIndicator
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let kanji = kanjiList[indexPath.row]
delegate?.kanjiListViewController(self, didSelectKanji: kanji)
tableView.deselectRow(at: indexPath, animated: true)
}
}
12. KanjiStateMachine.swift
import UIKit
import GameplayKit.GKStateMachine
class KanjiStateMachine: GKStateMachine {
let presenter: UINavigationController
let kanjiStorage: KanjiStorage
init(presenter: UINavigationController,
kanjiStorage: KanjiStorage,
states: [GKState]) {
// 1
self.presenter = presenter
// 2
self.kanjiStorage = kanjiStorage
// 3
super.init(states: states)
}
}
13. AllState.swift
import GameplayKit.GKState
class AllState: GKState {
// 1
lazy var allKanjiListCoordinator = makeAllKanjiCoordinator()
// 2
override func didEnter(from previousState: GKState?) {
if previousState == nil {
allKanjiListCoordinator?.start()
} else {
(stateMachine as? KanjiStateMachine)?.presenter.popToRootViewController(animated: true)
}
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == DetailState.self
}
private func makeAllKanjiCoordinator() -> KanjiListCoordinator? {
// 3
guard let kanjiStateMachine = stateMachine as? KanjiStateMachine else {
return nil
}
let kanjiStorage = kanjiStateMachine.kanjiStorage
// 4
return KanjiListCoordinator(presenter: kanjiStateMachine.presenter,
kanjiStorage: kanjiStorage,
list: kanjiStorage.allKanji(),
title: "Kanji List")
}
}
14. DetailState.swift
import GameplayKit.GKState
class DetailState: GKState {
// 1
var kanji: Kanji?
var kanjiDetailCoordinator: KanjiDetailCoordinator?
override func didEnter(from previousState: GKState?) {
guard
let kanji = kanji,
let kanjiStateMachine = (stateMachine as? KanjiStateMachine)
else {
return
}
// 2
let kanjiDetailCoordinator = KanjiDetailCoordinator(
presenter: kanjiStateMachine.presenter,
kanji: kanji,
kanjiStorage: kanjiStateMachine.kanjiStorage)
self.kanjiDetailCoordinator = kanjiDetailCoordinator
kanjiDetailCoordinator.start()
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == AllState.self || stateClass == ListState.self
}
}
15. ListState.swift
import GameplayKit.GKState
class ListState: GKState {
// 1
var word: String?
var kanjiListCoordinator: KanjiListCoordinator?
override func didEnter(from previousState: GKState?) {
guard
let word = word,
let kanjiStateMachine = (stateMachine as? KanjiStateMachine)
else {
return
}
let kanjiStorage = kanjiStateMachine.kanjiStorage
// 2
let kanjiForWord = kanjiStorage.kanjiForWord(word)
// 3
let kanjiListCoordinator = KanjiListCoordinator(
presenter: kanjiStateMachine.presenter, kanjiStorage: kanjiStorage,
list: kanjiForWord, title: word)
self.kanjiListCoordinator = kanjiListCoordinator
kanjiListCoordinator.start()
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
return stateClass == DetailState.self || stateClass == AllState.self
}
}
后記
本篇主要講述了GameplayKit的實用狀態(tài)機,感興趣的給個贊或者關注~~~