functionality for positioning cards and using view-controller containment
This commit is contained in:
parent
44c3440d59
commit
cd99ccda68
|
@ -22,6 +22,8 @@
|
||||||
F3857EB8222E4D27009A33C4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3857EB2222E4D27009A33C4 /* AppDelegate.swift */; };
|
F3857EB8222E4D27009A33C4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3857EB2222E4D27009A33C4 /* AppDelegate.swift */; };
|
||||||
F3857EBC222E517E009A33C4 /* PhantomFromSpace.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F3857EBA222E517E009A33C4 /* PhantomFromSpace.mp3 */; };
|
F3857EBC222E517E009A33C4 /* PhantomFromSpace.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = F3857EBA222E517E009A33C4 /* PhantomFromSpace.mp3 */; };
|
||||||
F3857EBD222E517E009A33C4 /* README.txt in Resources */ = {isa = PBXBuildFile; fileRef = F3857EBB222E517E009A33C4 /* README.txt */; };
|
F3857EBD222E517E009A33C4 /* README.txt in Resources */ = {isa = PBXBuildFile; fileRef = F3857EBB222E517E009A33C4 /* README.txt */; };
|
||||||
|
F3857EBF222E5BEB009A33C4 /* CardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3857EBE222E5BEB009A33C4 /* CardViewController.swift */; };
|
||||||
|
F3857EC1222E5DD9009A33C4 /* Dimension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3857EC0222E5DD9009A33C4 /* Dimension.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -86,6 +88,8 @@
|
||||||
F3857EB2222E4D27009A33C4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
F3857EB2222E4D27009A33C4 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
F3857EBA222E517E009A33C4 /* PhantomFromSpace.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = PhantomFromSpace.mp3; sourceTree = "<group>"; };
|
F3857EBA222E517E009A33C4 /* PhantomFromSpace.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = PhantomFromSpace.mp3; sourceTree = "<group>"; };
|
||||||
F3857EBB222E517E009A33C4 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = "<group>"; };
|
F3857EBB222E517E009A33C4 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = "<group>"; };
|
||||||
|
F3857EBE222E5BEB009A33C4 /* CardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardViewController.swift; sourceTree = "<group>"; };
|
||||||
|
F3857EC0222E5DD9009A33C4 /* Dimension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dimension.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -163,6 +167,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F3857EA9222E4D27009A33C4 /* Info.plist */,
|
F3857EA9222E4D27009A33C4 /* Info.plist */,
|
||||||
|
F3857EC0222E5DD9009A33C4 /* Dimension.swift */,
|
||||||
);
|
);
|
||||||
path = "Supporting Files";
|
path = "Supporting Files";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -180,6 +185,7 @@
|
||||||
children = (
|
children = (
|
||||||
F3857EAC222E4D27009A33C4 /* HomeViewController.swift */,
|
F3857EAC222E4D27009A33C4 /* HomeViewController.swift */,
|
||||||
F3857EAD222E4D27009A33C4 /* Home.storyboard */,
|
F3857EAD222E4D27009A33C4 /* Home.storyboard */,
|
||||||
|
F3857EBE222E5BEB009A33C4 /* CardViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Home;
|
path = Home;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -347,8 +353,10 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
F3857EC1222E5DD9009A33C4 /* Dimension.swift in Sources */,
|
||||||
F3857EB8222E4D27009A33C4 /* AppDelegate.swift in Sources */,
|
F3857EB8222E4D27009A33C4 /* AppDelegate.swift in Sources */,
|
||||||
F3857EB4222E4D27009A33C4 /* HomeViewController.swift in Sources */,
|
F3857EB4222E4D27009A33C4 /* HomeViewController.swift in Sources */,
|
||||||
|
F3857EBF222E5BEB009A33C4 /* CardViewController.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// CardViewController.swift
|
||||||
|
// ESP Tester
|
||||||
|
//
|
||||||
|
// Created by Brian Sipple on 3/5/19.
|
||||||
|
// Copyright © 2019 Brian Sipple. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class CardViewController: UIViewController {
|
||||||
|
// MARK: - Instance Properties
|
||||||
|
|
||||||
|
weak var delegate: UIViewController!
|
||||||
|
|
||||||
|
lazy var frontImageView = makeFrontImageView()
|
||||||
|
lazy var backImageView = makeBackImageView()
|
||||||
|
|
||||||
|
var isCorrect = false
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// Do any additional setup after loading the view.
|
||||||
|
view.bounds = CGRect(x: 0, y: 0, width: Dimension.Card.width, height: Dimension.Card.height)
|
||||||
|
|
||||||
|
view.addSubview(frontImageView)
|
||||||
|
view.addSubview(backImageView)
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
||||||
|
self?.backImageView.alpha = 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// MARK: - Navigation
|
||||||
|
|
||||||
|
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
// Get the new view controller using segue.destination.
|
||||||
|
// Pass the selected object to the new view controller.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Private functions
|
||||||
|
|
||||||
|
private func makeFrontImageView() -> UIImageView {
|
||||||
|
let imageView = makeCardImageView()
|
||||||
|
|
||||||
|
imageView.isHidden = true
|
||||||
|
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func makeBackImageView() -> UIImageView {
|
||||||
|
let imageView = makeCardImageView()
|
||||||
|
|
||||||
|
imageView.alpha = 0
|
||||||
|
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func makeCardImageView() -> UIImageView {
|
||||||
|
guard let image = UIImage(named: "cardBack") else {
|
||||||
|
fatalError("Failed to load card image assets")
|
||||||
|
}
|
||||||
|
|
||||||
|
let imageView = UIImageView(image: image)
|
||||||
|
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,9 +18,9 @@
|
||||||
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
|
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AyX-vP-N69">
|
<view autoresizesSubviews="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AyX-vP-N69">
|
||||||
<rect key="frame" x="208" y="47" width="480" height="320"/>
|
<rect key="frame" x="208" y="47" width="480" height="320"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="480" id="2eA-y0-2Fv"/>
|
<constraint firstAttribute="width" constant="480" id="2eA-y0-2Fv"/>
|
||||||
<constraint firstAttribute="height" constant="320" id="tf9-2o-Gez"/>
|
<constraint firstAttribute="height" constant="320" id="tf9-2o-Gez"/>
|
||||||
|
@ -34,6 +34,9 @@
|
||||||
</constraints>
|
</constraints>
|
||||||
<viewLayoutGuide key="safeArea" id="Uee-0j-aNJ"/>
|
<viewLayoutGuide key="safeArea" id="Uee-0j-aNJ"/>
|
||||||
</view>
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="cardContainer" destination="AyX-vP-N69" id="QJh-gr-bcX"/>
|
||||||
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
|
|
|
@ -9,12 +9,100 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class HomeViewController: UIViewController {
|
class HomeViewController: UIViewController {
|
||||||
|
@IBOutlet var cardContainer: UIView!
|
||||||
|
|
||||||
|
// MARK: - Instance Properties
|
||||||
|
|
||||||
|
var cardViewControllers: [CardViewController] = []
|
||||||
|
|
||||||
|
lazy var cardPositions = makeCardPositions()
|
||||||
|
lazy var cardImages = makeCardImages()
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
// Do any additional setup after loading the view, typically from a nib.
|
// Do any additional setup after loading the view, typically from a nib.
|
||||||
|
|
||||||
|
loadCards()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
@objc func loadCards() {
|
||||||
|
removeCardsInView()
|
||||||
|
cardImages.shuffle()
|
||||||
|
|
||||||
|
for (index, position) in cardPositions.enumerated() {
|
||||||
|
let cardViewController = CardViewController()
|
||||||
|
let cardImage = cardImages[index]
|
||||||
|
|
||||||
|
cardViewController.delegate = self
|
||||||
|
|
||||||
|
// use view controller containment...
|
||||||
|
addChild(cardViewController)
|
||||||
|
|
||||||
|
// ...AND add the VC's view to our container view
|
||||||
|
cardContainer.addSubview(cardViewController.view)
|
||||||
|
cardViewController.didMove(toParent: self)
|
||||||
|
|
||||||
|
cardViewController.view.center = position
|
||||||
|
cardViewController.frontImageView.image = cardImage
|
||||||
|
|
||||||
|
// if the new card is a star, mark is as "correct"
|
||||||
|
if cardImage.accessibilityIdentifier == "star" {
|
||||||
|
cardViewController.isCorrect = true
|
||||||
|
}
|
||||||
|
|
||||||
|
cardViewControllers.append(cardViewController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeCardsInView() {
|
||||||
|
for card in cardViewControllers {
|
||||||
|
card.view.removeFromSuperview()
|
||||||
|
card.removeFromParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
cardViewControllers.removeAll(keepingCapacity: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private functions
|
||||||
|
|
||||||
|
private func makeCardPositions() -> [CGPoint] {
|
||||||
|
let cardWidth = Dimension.Card.width
|
||||||
|
let cardHeight = Dimension.Card.height
|
||||||
|
let xSpacing = 10
|
||||||
|
let ySpacing = 10
|
||||||
|
|
||||||
|
var positions: [CGPoint] = []
|
||||||
|
|
||||||
|
for row in 0...1 {
|
||||||
|
let yPos = (cardHeight / 2) + (cardHeight * row) + (ySpacing * row) + 15
|
||||||
|
|
||||||
|
positions += [
|
||||||
|
CGPoint(x: (cardWidth / 2) + (cardWidth * 0) + (xSpacing * 1) + 15, y: yPos),
|
||||||
|
CGPoint(x: (cardWidth / 2) + (cardWidth * 1) + (xSpacing * 2) + 15, y: yPos),
|
||||||
|
CGPoint(x: (cardWidth / 2) + (cardWidth * 2) + (xSpacing * 3) + 15, y: yPos),
|
||||||
|
CGPoint(x: (cardWidth / 2) + (cardWidth * 3) + (xSpacing * 4) + 15, y: yPos),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func makeCardImages() -> [UIImage] {
|
||||||
|
return [
|
||||||
|
"Circle", "Circle", "Cross", "Cross", "Lines", "Lines", "Square", "Star"
|
||||||
|
].map {
|
||||||
|
let image = UIImage(named: "card\($0)")!
|
||||||
|
image.accessibilityIdentifier = $0
|
||||||
|
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// Dimension.swift
|
||||||
|
// ESP Tester
|
||||||
|
//
|
||||||
|
// Created by Brian Sipple on 3/5/19.
|
||||||
|
// Copyright © 2019 Brian Sipple. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
enum Dimension {
|
||||||
|
enum MainView {
|
||||||
|
static let width = 480
|
||||||
|
static let height = 320
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Card {
|
||||||
|
static let width = 100
|
||||||
|
static let height = 140
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@
|
||||||
| 34 | 🎮<br>[Four in a Row](/34-gamekit-four-in-a-row) | GameplayKit AI, GKGameModel, GKGameModelPlayer, GKGameModelUpdate, AI Heuristics, NSCopying, GKMinmaxStrategist | ✅ |
|
| 34 | 🎮<br>[Four in a Row](/34-gamekit-four-in-a-row) | GameplayKit AI, GKGameModel, GKGameModelPlayer, GKGameModelUpdate, AI Heuristics, NSCopying, GKMinmaxStrategist | ✅ |
|
||||||
| 35 | 🛠<br>[Random Numbers](/35-random-numbers) | Int.random(in:), Float.random(in:), Double.random(in:), CGFloat.random(in:), Bool.random(), arc4random(), GKRandomSource.sharedRandom(), GKLinearCongruentialRandomSource, GKMersenneTwisterRandomSource, GKARC4RandomSource, GKRandomDistribution, GKShuffledDistribution, GKGaussianDistribution, Fisher-Yates Algorithm, arrayByShufflingObjects(in:), the importance of being able to seed the source of randomness 🌱| ✅ |
|
| 35 | 🛠<br>[Random Numbers](/35-random-numbers) | Int.random(in:), Float.random(in:), Double.random(in:), CGFloat.random(in:), Bool.random(), arc4random(), GKRandomSource.sharedRandom(), GKLinearCongruentialRandomSource, GKMersenneTwisterRandomSource, GKARC4RandomSource, GKRandomDistribution, GKShuffledDistribution, GKGaussianDistribution, Fisher-Yates Algorithm, arrayByShufflingObjects(in:), the importance of being able to seed the source of randomness 🌱| ✅ |
|
||||||
| 36 | 🎮<br>[Crashy Plane](/36-crashy-plane) | scale modes, parallax scrolling, SpriteKit Physics, SKPhysicsContactDelegate, SKPhysicsBody, SKAudioNode, managing game state with enums, restarting entire game scenes | ✅ |
|
| 36 | 🎮<br>[Crashy Plane](/36-crashy-plane) | scale modes, parallax scrolling, SpriteKit Physics, SKPhysicsContactDelegate, SKPhysicsBody, SKAudioNode, managing game state with enums, restarting entire game scenes | ✅ |
|
||||||
| 37 | 🎮<br>[Psychic Tester](/37-psychic-tester) | WatchKit Extensions, 3D Touch, CAEmitterLayer, CAGradientLayer, @IBDesignable, @IBInspectable, transition(with:), WCSession, WKInterfaceLabel, WKInterfaceButton | 🚧 |
|
| 37 | 🎮<br>[WatchKit ESP Tester](/37-watchkit-game-esp-tester) | View Controller containment, WatchKit Extensions, 3D Touch, CAEmitterLayer, CAGradientLayer, @IBDesignable, @IBInspectable, transition(with:), WCSession, WKInterfaceLabel, WKInterfaceButton | 🚧 |
|
||||||
| 38 | 🛠<br>[Github Commits (Core Data)](/38-githubcommits) | NSFetchRequest, NSManagedObject, NSPredicate, NSSortDescriptor, NSFetchedResultsController, ISO8601DateFormatter | 🔴 |
|
| 38 | 🛠<br>[Github Commits (Core Data)](/38-githubcommits) | NSFetchRequest, NSManagedObject, NSPredicate, NSSortDescriptor, NSFetchedResultsController, ISO8601DateFormatter | 🔴 |
|
||||||
| 39 | 🛠<br>[Unit testing with XCTest](/39-swift-unit-tests) | XCTest, `filter()`, Test-Driven Development, Functional Programming, XCTestCase, Setting a Baseline, NSCountedSet, XCUIApplication(), XCUIElementQuery, UI Test Recording | 🔴 |
|
| 39 | 🛠<br>[Unit testing with XCTest](/39-swift-unit-tests) | XCTest, `filter()`, Test-Driven Development, Functional Programming, XCTestCase, Setting a Baseline, NSCountedSet, XCUIApplication(), XCUIElementQuery, UI Test Recording | 🔴 |
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue