Skip to content

Commit

Permalink
Split views now implemented on Practice and News. Still needs a lot m…
Browse files Browse the repository at this point in the history
…ore work, though.
  • Loading branch information
twostraws committed May 2, 2019
1 parent a7d54c6 commit a080d87
Show file tree
Hide file tree
Showing 23 changed files with 191 additions and 47 deletions.
10 changes: 9 additions & 1 deletion Unwrap.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
51A6D66A20E6954400FB6B46 /* Replacement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A6D66920E6954400FB6B46 /* Replacement.swift */; };
51A6D66C20E696A300FB6B46 /* SpotTheErrorPractice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A6D66B20E696A300FB6B46 /* SpotTheErrorPractice.swift */; };
51A90DC0227A60AA0068A4F9 /* UISplitViewController-NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A90DBF227A60AA0068A4F9 /* UISplitViewController-NavigationController.swift */; };
51A90DC2227AF0D60068A4F9 /* PleaseSelectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A90DC1227AF0D60068A4F9 /* PleaseSelectViewController.swift */; };
51A90DC4227AFC060068A4F9 /* SplitViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A90DC3227AFC060068A4F9 /* SplitViewControllerDelegate.swift */; };
51ADAEA520CE9004000D9DCF /* ChallengeResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51ADAEA420CE9004000D9DCF /* ChallengeResult.swift */; };
51B3A7C820CCC74B000ACA6D /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B3A7C720CCC74B000ACA6D /* MainTabBarController.swift */; };
51B3A7CA20CCCAF2000ACA6D /* HomeDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B3A7C920CCCAF2000ACA6D /* HomeDataSource.swift */; };
Expand Down Expand Up @@ -783,6 +785,8 @@
51A6D66920E6954400FB6B46 /* Replacement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Replacement.swift; sourceTree = "<group>"; };
51A6D66B20E696A300FB6B46 /* SpotTheErrorPractice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotTheErrorPractice.swift; sourceTree = "<group>"; };
51A90DBF227A60AA0068A4F9 /* UISplitViewController-NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISplitViewController-NavigationController.swift"; sourceTree = "<group>"; };
51A90DC1227AF0D60068A4F9 /* PleaseSelectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PleaseSelectViewController.swift; sourceTree = "<group>"; };
51A90DC3227AFC060068A4F9 /* SplitViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewControllerDelegate.swift; sourceTree = "<group>"; };
51ADAEA420CE9004000D9DCF /* ChallengeResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChallengeResult.swift; sourceTree = "<group>"; };
51B3A7C720CCC74B000ACA6D /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = "<group>"; };
51B3A7C920CCCAF2000ACA6D /* HomeDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeDataSource.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1389,6 +1393,7 @@
5115CD65216B98C3006635FE /* EditingToolbarView.swift */,
51CC139C2065AB8500F37A67 /* GradientView.swift */,
51596DE920EE04EF00952ADC /* LeftAlignedCollectionViewFlowLayout.swift */,
51A90DC1227AF0D60068A4F9 /* PleaseSelectViewController.swift */,
51CC1390206560A600F37A67 /* ShapeView.swift */,
511FD6BF2107425F0023E92C /* TappableTextView.swift */,
);
Expand Down Expand Up @@ -1646,10 +1651,11 @@
5156459220E116EE00CED877 /* Content */,
51CC13622065607C00F37A67 /* AppDelegate.swift */,
51B3A7C720CCC74B000ACA6D /* MainTabBarController.swift */,
51A90DC3227AFC060068A4F9 /* SplitViewControllerDelegate.swift */,
511FD6B221065BF80023E92C /* Unwrap.swift */,
51CC13662065607C00F37A67 /* Main.storyboard */,
51CC136B2065607F00F37A67 /* LaunchScreen.storyboard */,
51CC136E2065607F00F37A67 /* Info.plist */,
511FD6B221065BF80023E92C /* Unwrap.swift */,
);
path = Unwrap;
sourceTree = "<group>";
Expand Down Expand Up @@ -2952,6 +2958,7 @@
518FAE7420E3C067007F4675 /* AlertViewController.swift in Sources */,
51596DEE20EE2B5B00952ADC /* TapToCodeDataSource.swift in Sources */,
5189D9DF20EADA3E00E6E1D3 /* UIView-EditingToolbar.swift in Sources */,
51A90DC4227AFC060068A4F9 /* SplitViewControllerDelegate.swift in Sources */,
5153F4A820EFCD29009E829D /* Skippable.swift in Sources */,
511FD6BE210740860023E92C /* HelpTableViewCell.swift in Sources */,
51A6D62520E651DA00FB6B46 /* BoolNames.swift in Sources */,
Expand Down Expand Up @@ -2981,6 +2988,7 @@
51053A002113332A00B28328 /* TourItemViewController.swift in Sources */,
511FD6B621065CED0023E92C /* HelpViewController.swift in Sources */,
51596DE820EE04A500952ADC /* TapToCodeViewController.swift in Sources */,
51A90DC2227AF0D60068A4F9 /* PleaseSelectViewController.swift in Sources */,
51A90DC0227A60AA0068A4F9 /* UISplitViewController-NavigationController.swift in Sources */,
511FD6C02107425F0023E92C /* TappableTextView.swift in Sources */,
51596DFB20EE94DC00952ADC /* AwardPointsViewController.swift in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Unwrap/Activities/Awards/AwardPointsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class AwardPointsViewController: UIViewController, Storyboarded {
super.viewDidLoad()

assert(coordinator != nil, "You must set a coordinator before presenting this view controller.")
extendedLayoutIncludesOpaqueBars = true

totalPoints.title = "TOTAL"
earnedPoints.title = "EARNED"
Expand Down
2 changes: 1 addition & 1 deletion Unwrap/Activities/Challenges/ChallengesCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ChallengesCoordinator: Coordinator, Awarding, Skippable, AnswerHandling {
/// Moves the current challenge to the next question, or ends it if there are no more questions.
func askQuestion() {
if let currentQuestion = questions.popLast() {
var viewController = currentQuestion.instantiate()
let viewController = currentQuestion.instantiate()
viewController.coordinator = self
viewController.questionNumber = 10 - questions.count
navigationController.pushViewController(viewController, animated: true)
Expand Down
25 changes: 9 additions & 16 deletions Unwrap/Activities/Learn/LearnCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,37 @@ import UIKit

/// Manages everything launched from the Learn tab in the app.
class LearnCoordinator: Coordinator, Awarding, Skippable, AlertHandling, AnswerHandling, UISplitViewControllerDelegate {
var splitViewController: UISplitViewController
var navigationController: CoordinatedNavigationController
var splitViewController = UISplitViewController()
var primaryNavigationController = CoordinatedNavigationController()
var activeStudyReview: StudyReview!

/// Whether or not the user can have multiple attempts at questions
let retriesAllowed = true

init(navigationController: CoordinatedNavigationController = CoordinatedNavigationController()) {
self.splitViewController = UISplitViewController()

init() {
// Set up the master view controller
self.navigationController = navigationController
navigationController.navigationBar.prefersLargeTitles = true
navigationController.coordinator = self
primaryNavigationController.navigationBar.prefersLargeTitles = true
primaryNavigationController.coordinator = self

let viewController = LearnViewController(style: .plain)
viewController.coordinator = self
navigationController.viewControllers = [viewController]
primaryNavigationController.viewControllers = [viewController]

// Set up the detail view controller
let detailNavigationController = UINavigationController(rootViewController: studyViewController(for: "Variables"))

splitViewController.viewControllers = [navigationController, detailNavigationController]
splitViewController.viewControllers = [primaryNavigationController, detailNavigationController]
splitViewController.tabBarItem = UITabBarItem(title: "Learn", image: UIImage(bundleName: "Learn"), tag: 1)

// make this split view controller behave sensibly on iPad
splitViewController.preferredDisplayMode = .allVisible
splitViewController.delegate = self
splitViewController.delegate = SplitViewControllerDelegate.shared
}

/// Shows the list of common Swift terms
func showGlossary() {
let vc = GlossaryViewController(style: .plain)
navigationController.pushViewController(vc, animated: true)
primaryNavigationController.pushViewController(vc, animated: true)
}

/// Triggered when we already have a study view controller configured and ready to go, so we just show it.
Expand Down Expand Up @@ -208,8 +205,4 @@ class LearnCoordinator: Coordinator, Awarding, Skippable, AlertHandling, AnswerH
let detailNav = UINavigationController(rootViewController: viewController)
splitViewController.showDetailViewController(detailNav, sender: self)
}

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
return true
}
}
1 change: 1 addition & 0 deletions Unwrap/Activities/Learn/LearnViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class LearnViewController: UITableViewController, UserTracking, UIViewController
title = "Learn"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Glossary", style: .plain, target: self, action: #selector(showGlossary))
registerForUserChanges()
extendedLayoutIncludesOpaqueBars = true

tableView.dataSource = dataSource
tableView.delegate = dataSource
Expand Down
1 change: 1 addition & 0 deletions Unwrap/Activities/Learn/Review/ReviewViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ReviewViewController: UIViewController, AlertShowing, PracticingViewContro
navigationItem.largeTitleDisplayMode = .never
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Skip", style: .plain, target: self, action: #selector(skip))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Hint", style: .plain, target: self, action: #selector(hint))
extendedLayoutIncludesOpaqueBars = true
}

override func viewDidLoad() {
Expand Down
1 change: 1 addition & 0 deletions Unwrap/Activities/Learn/Study/StudyViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class StudyViewController: UIViewController, TappableTextViewDelegate {

assert(coordinator != nil, "You must set a coordinator before presenting this view controller.")

extendedLayoutIncludesOpaqueBars = true
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: coordinator, action: #selector(LearnCoordinator.finishedStudying))

// always include the safe area insets in the scroll view content adjustment
Expand Down
27 changes: 18 additions & 9 deletions Unwrap/Activities/News/NewsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,30 @@ import UIKit
/// Manages everything launched from the News tab in the app.
class NewsCoordinator: Coordinator {
var splitViewController = UISplitViewController()
var navigationController: CoordinatedNavigationController
var primaryNavigationController = CoordinatedNavigationController()

init(navigationController: CoordinatedNavigationController = CoordinatedNavigationController()) {
self.navigationController = navigationController
navigationController.navigationBar.prefersLargeTitles = true
navigationController.coordinator = self
init() {
// Set up the master view controller
primaryNavigationController.navigationBar.prefersLargeTitles = true
primaryNavigationController.coordinator = self

let viewController = NewsViewController(style: .plain)
viewController.tabBarItem = UITabBarItem(title: "News", image: UIImage(bundleName: "News"), tag: 4)
viewController.coordinator = self

navigationController.viewControllers = [viewController]
primaryNavigationController.viewControllers = [viewController]

// force our view controller to load immediately, so we download the news in the background rather than waiting for users to go to the tab
viewController.loadViewIfNeeded()

// Set up the detail view controller
let detailViewController = PleaseSelectViewController.instantiate()
detailViewController.selectionMode = .news

splitViewController.viewControllers = [primaryNavigationController, detailViewController]
splitViewController.tabBarItem = UITabBarItem(title: "News", image: UIImage(bundleName: "News"), tag: 4)

// make this split view controller behave sensibly on iPad
splitViewController.preferredDisplayMode = .allVisible
}

/// Creates and configures – but does not show! – a Safari view controller for a specific article. This might be called when the user tapped a story, or when they 3D touch one.
Expand All @@ -37,7 +46,7 @@ class NewsCoordinator: Coordinator {

/// Triggered when we already have a Safari view controller configured and ready to go, so we just show it.
func startReading(using viewController: UIViewController, withURL url: URL) {
navigationController.present(viewController, animated: true)
splitViewController.showDetailViewController(viewController, sender: self)
User.current.readNewsStory(forURL: url)
}

Expand All @@ -51,6 +60,6 @@ class NewsCoordinator: Coordinator {
@objc func buyBooks() {
let storeURL = URL(staticString: "https://www.hackingwithswift.com/store")
let viewController = SFSafariViewController(url: storeURL)
navigationController.present(viewController, animated: true)
splitViewController.showDetailViewController(viewController, sender: self)
}
}
1 change: 1 addition & 0 deletions Unwrap/Activities/News/NewsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class NewsViewController: UITableViewController, UIViewControllerPreviewingDeleg

title = "News"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Buy Swift Books", style: .plain, target: coordinator, action: #selector(NewsCoordinator.buyBooks))
extendedLayoutIncludesOpaqueBars = true

dataSource.delegate = self
tableView.dataSource = dataSource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class FreeCodingViewController: UIViewController, Storyboarded, PracticingViewCo

title = "Free Coding" + (coordinator?.titleSuffix(for: self) ?? "")
navigationItem.largeTitleDisplayMode = .never
extendedLayoutIncludesOpaqueBars = true

// The prompt can only be simple HTML (e.g. <code></code>), but the source code is fully syntax highlighted.
prompt.attributedText = practiceData.question.fromSimpleHTML()
Expand Down
39 changes: 25 additions & 14 deletions Unwrap/Activities/Practice/PracticeCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import UIKit
/// Manages everything launched from the Practice tab in the app.
class PracticeCoordinator: Coordinator, Awarding, Skippable, AnswerHandling {
var splitViewController = UISplitViewController()
var navigationController: CoordinatedNavigationController
var primaryNavigationController = CoordinatedNavigationController()

/// Stores whichever activity the user is currently taking, so that we can make new instances of it when working through a practice sequence.
var currentActivity: PracticeActivity.Type?
Expand All @@ -22,15 +22,24 @@ class PracticeCoordinator: Coordinator, Awarding, Skippable, AnswerHandling {
/// Whether or not the user can have multiple attempts at questions
let retriesAllowed = true

init(navigationController: CoordinatedNavigationController = CoordinatedNavigationController()) {
self.navigationController = navigationController
navigationController.navigationBar.prefersLargeTitles = true
navigationController.coordinator = self
init() {
// Set up the master view controller
primaryNavigationController.navigationBar.prefersLargeTitles = true
primaryNavigationController.coordinator = self

let viewController = PracticeViewController(style: .plain)
viewController.tabBarItem = UITabBarItem(title: "Practice", image: UIImage(bundleName: "Practice"), tag: 2)
viewController.coordinator = self
navigationController.viewControllers = [viewController]
primaryNavigationController.viewControllers = [viewController]

// Set up the detail view controller
// let detailNavigationController = UINavigationController(rootViewController: PleaseSelectViewController.instantiate())

splitViewController.viewControllers = [primaryNavigationController, PleaseSelectViewController.instantiate()]
splitViewController.tabBarItem = UITabBarItem(title: "Practice", image: UIImage(bundleName: "Practice"), tag: 2)

// make this split view controller behave sensibly on iPad
splitViewController.preferredDisplayMode = .allVisible
splitViewController.delegate = SplitViewControllerDelegate.shared
}

/// Called when the user wants to start practicing something. We either start it immediately, or show an alert refusing access if they haven't completed the required learn chapter.
Expand All @@ -39,24 +48,26 @@ class PracticeCoordinator: Coordinator, Awarding, Skippable, AnswerHandling {
// They can't access this practice activity yet.
let alert = UIAlertController(title: "Activity Locked", message: "You need to complete the chapter \"\(activity.lockedUntil)\" before you can practice this.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
navigationController.present(alert, animated: true)
splitViewController.present(alert, animated: true)
return false
} else {
// They can access this activity, so clear our state and begin immediately.
currentActivity = activity
currentScore = 0

var viewController = activity.instantiate()
let viewController = activity.instantiate()
viewController.coordinator = self
navigationController.pushViewController(viewController, animated: true)

let detailNav = UINavigationController(rootViewController: viewController)
splitViewController.showDetailViewController(detailNav, sender: self)

return true
}
}

/// Called when the user has submitted an answer from a practice question.
func answerSubmitted(from: UIViewController, wasCorrect: Bool) {
guard let practicingViewController = from as? PracticingViewController else {
func answerSubmitted(from viewController: UIViewController, wasCorrect: Bool) {
guard let practicingViewController = viewController as? PracticingViewController else {
fatalError("Unknown view controller trying to submit a practice answer.")
}

Expand All @@ -70,14 +81,14 @@ class PracticeCoordinator: Coordinator, Awarding, Skippable, AnswerHandling {
finishedPracticing(type: practicingViewController.practiceType)
} else {
// We're not finished yet, so make a fresh instance of our practice type, move its question number along one, then show it.
guard var viewController = currentActivity?.instantiate() else {
guard let viewController = currentActivity?.instantiate() else {
fatalError("Trying to instantiate an empty practice activity.")
}

viewController.questionNumber = practicingViewController.questionNumber + 1
viewController.coordinator = self

navigationController.pushViewController(viewController, animated: true)
practicingViewController.navigationController?.pushViewController(viewController, animated: true)
}
}

Expand Down
1 change: 1 addition & 0 deletions Unwrap/Activities/Practice/PracticeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class PracticeViewController: UITableViewController, UserTracking {
registerForUserChanges()
tableView.dataSource = dataSource
tableView.register(PracticeTableViewCell.self, forCellReuseIdentifier: "Cell")
extendedLayoutIncludesOpaqueBars = true
}

/// Refreshes everything when the user changes.
Expand Down
2 changes: 1 addition & 1 deletion Unwrap/Activities/Practice/PracticingViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit

/// Each of our practice view controllers must have a coordinator, a question number (so we know its position in the sequence), and a practice type so that we can track how many times it has been used.
protocol PracticingViewController: AlertShowing, Sequenced {
protocol PracticingViewController: UIViewController, AlertShowing, Sequenced {
var coordinator: (Skippable & AnswerHandling)? { get set }
var questionNumber: Int { get set }
var practiceType: String { get }
Expand Down
Loading

0 comments on commit a080d87

Please sign in to comment.