From 03f5c4a1a3c43a2cd85974190719ebffb92387b9 Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 12 Jul 2018 11:12:23 +0900 Subject: [PATCH 1/5] Convert to Swift 4 --- .../MemoryGame.xcodeproj/project.pbxproj | 2 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + MemoryGame/MemoryGame/AppDelegate.swift | 66 ++-- .../AppIcon.appiconset/Contents.json | 15 + .../MemoryGame/Base.lproj/Main.storyboard | 82 ++--- .../CustomizeCardsTableViewController.swift | 263 +++++++------- .../Controller/GameViewController.swift | 339 +++++++++--------- .../ScoresTableViewController.swift | 108 +++--- .../MemoryGame/Extensions/Array+Shuffle.swift | 14 +- .../Extensions/UIImage+Downloader.swift | 30 +- MemoryGame/MemoryGame/Model/Card.swift | 4 +- MemoryGame/MemoryGame/Model/Highscores.swift | 50 +-- MemoryGame/MemoryGame/Model/MemoryGame.swift | 219 ++++++----- MemoryGame/MemoryGame/View/CardCVC.swift | 94 ++--- 14 files changed, 656 insertions(+), 638 deletions(-) create mode 100644 MemoryGame/MemoryGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/MemoryGame/MemoryGame.xcodeproj/project.pbxproj b/MemoryGame/MemoryGame.xcodeproj/project.pbxproj index 6186b2d..73df885 100644 --- a/MemoryGame/MemoryGame.xcodeproj/project.pbxproj +++ b/MemoryGame/MemoryGame.xcodeproj/project.pbxproj @@ -318,6 +318,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.danielts.MemoryGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -331,6 +332,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.danielts.MemoryGame; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/MemoryGame/MemoryGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MemoryGame/MemoryGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MemoryGame/MemoryGame.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MemoryGame/MemoryGame/AppDelegate.swift b/MemoryGame/MemoryGame/AppDelegate.swift index c71e73d..61f9e7a 100644 --- a/MemoryGame/MemoryGame/AppDelegate.swift +++ b/MemoryGame/MemoryGame/AppDelegate.swift @@ -13,39 +13,37 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - // Override point for customization after application launch. - - //window?.tintColor = UIColor.whiteColor() - return true - } - - func applicationWillResignActive(application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. - } - - func applicationWillEnterForeground(application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillTerminate(application: UIApplication) { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - } - - + + var window: UIWindow? + + + private func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + + //window?.tintColor = UIColor.whiteColor() + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } } diff --git a/MemoryGame/MemoryGame/Assets.xcassets/AppIcon.appiconset/Contents.json b/MemoryGame/MemoryGame/Assets.xcassets/AppIcon.appiconset/Contents.json index 1024a77..7f4a31b 100644 --- a/MemoryGame/MemoryGame/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/MemoryGame/MemoryGame/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "size" : "29x29", "idiom" : "iphone", @@ -35,6 +45,11 @@ "idiom" : "iphone", "filename" : "AppIcon60x60@3x.png", "scale" : "3x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/MemoryGame/MemoryGame/Base.lproj/Main.storyboard b/MemoryGame/MemoryGame/Base.lproj/Main.storyboard index 6737896..c3dd1c2 100644 --- a/MemoryGame/MemoryGame/Base.lproj/Main.storyboard +++ b/MemoryGame/MemoryGame/Base.lproj/Main.storyboard @@ -1,8 +1,12 @@ - - + + + + + - + + @@ -14,63 +18,63 @@ - + - + - - + + @@ -91,16 +95,15 @@ - + - + - - + @@ -132,21 +135,21 @@ - + @@ -182,29 +185,29 @@ - + - + - + - + @@ -230,9 +233,8 @@ - - + @@ -248,19 +250,19 @@ - + - + - + - + - + @@ -272,7 +274,7 @@ - + @@ -304,5 +306,5 @@ - + diff --git a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift index 6af49c9..a827548 100644 --- a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift +++ b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift @@ -9,155 +9,152 @@ import UIKit class CustomizeCardsTableViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { - - // MARK: - Properties - - var cards: [UIImage] = MemoryGame.defaultCardImages + + // MARK: - Properties + + var cards: [UIImage] = MemoryGame.defaultCardImages + + var selectedIndexPath: IndexPath? + + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(false, animated: true) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(true, animated: true) + } + + // MARK: - Methods + + private func didPressChangeCard(index: Int) { + let alertController = UIAlertController(title: NSLocalizedString("Select card image source:", comment: "-"), message: nil, preferredStyle: .actionSheet) - var selectedIndexPath: NSIndexPath? - - // MARK: - Lifecycle - - override func viewDidLoad() { - super.viewDidLoad() - } - - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(false, animated: true) + if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { + let PhotoLibraryAction = UIAlertAction(title: NSLocalizedString("Photo Library", comment: "pl"), style: .default) { [weak self] (action) in + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .photoLibrary + self?.present(imagePicker, animated: true, completion: nil) + } + alertController.addAction(PhotoLibraryAction) } - override func viewWillDisappear(animated: Bool) { - super.viewWillDisappear(animated) - navigationController?.setNavigationBarHidden(true, animated: true) + if UIImagePickerController.isSourceTypeAvailable(.camera) { + let CameraAction = UIAlertAction(title: NSLocalizedString("Take Photo", comment: "tp"), style: .default) { [weak self] (action) in + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .camera + self?.present(imagePicker, animated: true, completion: nil) + } + alertController.addAction(CameraAction) } - // MARK: - Methods - - private func didPressChangeCard(index: Int) { - let alertController = UIAlertController(title: NSLocalizedString("Select card image source:", comment: "-"), message: nil, preferredStyle: .ActionSheet) - - if UIImagePickerController.isSourceTypeAvailable(.PhotoLibrary) { - let PhotoLibraryAction = UIAlertAction(title: NSLocalizedString("Photo Library", comment: "pl"), style: .Default) { [weak self] (action) in - let imagePicker = UIImagePickerController() - imagePicker.delegate = self - imagePicker.sourceType = .PhotoLibrary - self?.presentViewController(imagePicker, animated: true, completion: nil) - } - alertController.addAction(PhotoLibraryAction) - } - - if UIImagePickerController.isSourceTypeAvailable(.Camera) { - let CameraAction = UIAlertAction(title: NSLocalizedString("Take Photo", comment: "tp"), style: .Default) { [weak self] (action) in - let imagePicker = UIImagePickerController() - imagePicker.delegate = self - imagePicker.sourceType = .Camera - self?.presentViewController(imagePicker, animated: true, completion: nil) - } - alertController.addAction(CameraAction) - } - - let URLAction = UIAlertAction(title: NSLocalizedString("Insert URL", comment: "url"), style: .Default) { [weak self] (action) in - self?.promptImageURL() - } - alertController.addAction(URLAction) - - let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in } - alertController.addAction(cancelAction) - - self.presentViewController(alertController, animated: true) { } + let URLAction = UIAlertAction(title: NSLocalizedString("Insert URL", comment: "url"), style: .default) { [weak self] (action) in + self?.promptImageURL() } + alertController.addAction(URLAction) - private func promptImageURL() { - let alertController = UIAlertController(title: NSLocalizedString("Enter Image URL:", comment: "title"), message: nil, preferredStyle: .Alert) - - let enterUrlAction = UIAlertAction(title: NSLocalizedString("Load", comment: "load"), style: .Default) { [weak self] (_) in - let textField = alertController.textFields![0] as UITextField - guard let url = NSURL(string: textField.text!) else { return } - self?.loadImage(url) - } - enterUrlAction.enabled = false - alertController.addAction(enterUrlAction) - - alertController.addTextFieldWithConfigurationHandler { (textField) in - textField.placeholder = NSLocalizedString("Image URL", comment: "image url") - textField.keyboardType = .URL - - NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification, - object: textField, - queue: NSOperationQueue.mainQueue()) { (notification) in - enterUrlAction.enabled = textField.text != "" - } - } - - let cancelAction = UIAlertAction(title: NSLocalizedString("Dismiss", comment: "dismiss"), style: .Cancel) { (action) in } - alertController.addAction(cancelAction) - - self.presentViewController(alertController, animated: true) { } - } + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in } + alertController.addAction(cancelAction) - private func loadImage(url: NSURL) { - UIImage.downloadImage(url) { [weak self] (image: UIImage?) -> Void in - guard let image = image else { return } - self?.changeCard((self?.selectedIndexPath!.row)!, image: image) - } - } + self.present(alertController, animated: true) { } + } + + private func promptImageURL() { + let alertController = UIAlertController(title: NSLocalizedString("Enter Image URL:", comment: "title"), message: nil, preferredStyle: .alert) - private func changeCard(index: Int, image: UIImage) { - cards[index] = image - MemoryGame.defaultCardImages[index] = image - tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Automatic) + let enterUrlAction = UIAlertAction(title: NSLocalizedString("Load", comment: "load"), style: .default) { [weak self] (_) in + let textField = alertController.textFields![0] as UITextField + guard let url = NSURL(string: textField.text!) else { return } + self?.loadImage(url: url) } + enterUrlAction.isEnabled = false + alertController.addAction(enterUrlAction) - // MARK: - UITableViewDataSource - - override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 1 + alertController.addTextField { (textField) in + textField.placeholder = NSLocalizedString("Image URL", comment: "image url") + textField.keyboardType = .URL + + NotificationCenter.default.addObserver(forName:NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main, using: { + (notification) in + enterUrlAction.isEnabled = textField.text != "" + }) } - - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return cards.count - } - - override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCellWithIdentifier("cardCell", forIndexPath: indexPath) - - let textLabel: UILabel = cell.viewWithTag(1) as! UILabel - let imageView: UIImageView = cell.viewWithTag(2) as! UIImageView - - // Configure the cell... - let card = cards[indexPath.row] - - textLabel.text = String(format: "Card %d", indexPath.row+1) - imageView.image = card - - return cell - } + let cancelAction = UIAlertAction(title: NSLocalizedString("Dismiss", comment: "dismiss"), style: .cancel) { (action) in } + alertController.addAction(cancelAction) - // MARK: - UITableViewDelegate - - override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - tableView.deselectRowAtIndexPath(indexPath, animated: true) - - selectedIndexPath = indexPath - didPressChangeCard(indexPath.row) + self.present(alertController, animated: true) { } + } + + private func loadImage(url: NSURL) { + UIImage.downloadImage(url: url) { [weak self] (image: UIImage?) -> Void in + guard let image = image else { return } + self?.changeCard(index: (self?.selectedIndexPath!.row)!, image: image) } + } + + private func changeCard(index: Int, image: UIImage) { + cards[index] = image + MemoryGame.defaultCardImages[index] = image + tableView.reloadRows(at: [NSIndexPath(row: index, section: 0) as IndexPath], with: .automatic) + } + + // MARK: - UITableViewDataSource + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return cards.count + } + + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cardCell", for: indexPath as IndexPath) - // MARK: - UIImagePickerControllerDelegate - - func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { - picker.dismissViewControllerAnimated(true, completion: nil) - - guard let selectedIndexPath = selectedIndexPath else { return } - if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { - changeCard(selectedIndexPath.row, image: image) - } - } + let textLabel: UILabel = cell.viewWithTag(1) as! UILabel + let imageView: UIImageView = cell.viewWithTag(2) as! UIImageView - func imagePickerControllerDidCancel(picker: UIImagePickerController) { - picker.dismissViewControllerAnimated(true, completion: nil) + // Configure the cell... + let card = cards[indexPath.row] + + textLabel.text = String(format: "Card %d", indexPath.row+1) + imageView.image = card + + return cell + } + + // MARK: - UITableViewDelegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath as IndexPath, animated: true) + + selectedIndexPath = indexPath + didPressChangeCard(index: indexPath.row) + } + + // MARK: - UIImagePickerControllerDelegate + + private func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { + picker.dismiss(animated: true, completion: nil) + + guard let selectedIndexPath = selectedIndexPath else { return } + if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { + changeCard(index: selectedIndexPath.row, image: image) } - - + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true, completion: nil) + } } diff --git a/MemoryGame/MemoryGame/Controller/GameViewController.swift b/MemoryGame/MemoryGame/Controller/GameViewController.swift index fbeaab8..4225de4 100644 --- a/MemoryGame/MemoryGame/Controller/GameViewController.swift +++ b/MemoryGame/MemoryGame/Controller/GameViewController.swift @@ -9,176 +9,173 @@ import UIKit class GameViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, MemoryGameDelegate { - - // MARK: Properties - - @IBOutlet weak var collectionView: UICollectionView! - @IBOutlet weak var timerLabel: UILabel! - @IBOutlet weak var playButton: UIButton! - - let gameController = MemoryGame() - var timer:NSTimer? - - // MARK: - Lifecycle - - override func viewDidLoad() { - super.viewDidLoad() - - gameController.delegate = self - resetGame() - } - - override func viewDidDisappear(animated: Bool) { - super.viewDidDisappear(animated) - - if gameController.isPlaying { - resetGame() - } - } - - // MARK: - Methods - - func resetGame() { - gameController.stopGame() - if timer?.valid == true { - timer?.invalidate() - timer = nil - } - collectionView.userInteractionEnabled = false - collectionView.reloadData() - timerLabel.text = String(format: "%@: ---", NSLocalizedString("TIME", comment: "time")) - playButton.setTitle(NSLocalizedString("Play", comment: "play"), forState: .Normal) - } - - @IBAction func didPressPlayButton() { - if gameController.isPlaying { - resetGame() - playButton.setTitle(NSLocalizedString("Play", comment: "play"), forState: .Normal) - } else { - setupNewGame() - playButton.setTitle(NSLocalizedString("Stop", comment: "stop"), forState: .Normal) - } - } - - func setupNewGame() { - let cardsData:[UIImage] = MemoryGame.defaultCardImages - gameController.newGame(cardsData) - } - - func gameTimerAction() { - timerLabel.text = String(format: "%@: %.0fs", NSLocalizedString("TIME", comment: "time"), gameController.elapsedTime) - } - - func savePlayerScore(name: String, score: NSTimeInterval) { - Highscores.sharedInstance.saveHighscore(name, score: score) - } - - // MARK: - UICollectionViewDataSource - - func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { - return 1 - } - - func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return gameController.numberOfCards > 0 ? gameController.numberOfCards : 12 - } - - func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cardCell", forIndexPath: indexPath) as! CardCVC - cell.showCard(false, animted: false) - - guard let card = gameController.cardAtIndex(indexPath.item) else { return cell } - cell.card = card - - return cell - } - - // MARK: UICollectionViewDelegate - - func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { - let cell = collectionView.cellForItemAtIndexPath(indexPath) as! CardCVC - - if cell.shown { return } - gameController.didSelectCard(cell.card) - - collectionView.deselectItemAtIndexPath(indexPath, animated:true) - } - - // MARK: - UICollectionViewDataSource - - func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { - //let numberOfColumns:Int = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - - let itemWidth: CGFloat = CGRectGetWidth(collectionView.frame) / 3.0 - 15.0 //numberOfColumns as CGFloat - 10 //- (minimumInteritemSpacing * numberOfColumns)) - - return CGSizeMake(itemWidth, itemWidth) - } - - // MARK: - MemoryGameDelegate - - func memoryGameDidStart(game: MemoryGame) { - collectionView.reloadData() - collectionView.userInteractionEnabled = true - - timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "gameTimerAction", userInfo: nil, repeats: true) - - } - - func memoryGame(game: MemoryGame, showCards cards: [Card]) { - for card in cards { - guard let index = gameController.indexForCard(card) else { continue } - let cell = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection:0)) as! CardCVC - cell.showCard(true, animted: true) - } - } - - func memoryGame(game: MemoryGame, hideCards cards: [Card]) { - for card in cards { - guard let index = gameController.indexForCard(card) else { continue } - let cell = collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection:0)) as! CardCVC - cell.showCard(false, animted: true) - } - } - - - func memoryGameDidEnd(game: MemoryGame, elapsedTime: NSTimeInterval) { - timer?.invalidate() - - let elapsedTime = gameController.elapsedTime - - let alertController = UIAlertController( - title: NSLocalizedString("Hurrah!", comment: "title"), - message: String(format: "%@ %.0f seconds", NSLocalizedString("You finished the game in", comment: "message"), elapsedTime), - preferredStyle: .Alert) - - let saveScoreAction = UIAlertAction(title: NSLocalizedString("Save Score", comment: "save score"), style: .Default) { [weak self] (_) in - let nameTextField = alertController.textFields![0] as UITextField - guard let name = nameTextField.text else { return } - self?.savePlayerScore(name, score: elapsedTime) - self?.resetGame() - } - saveScoreAction.enabled = false - alertController.addAction(saveScoreAction) - - alertController.addTextFieldWithConfigurationHandler { (textField) in - textField.placeholder = NSLocalizedString("Your name", comment: "your name") - - NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification, - object: textField, - queue: NSOperationQueue.mainQueue()) { (notification) in - saveScoreAction.enabled = textField.text != "" - } - } - - let cancelAction = UIAlertAction(title: NSLocalizedString("Dismiss", comment: "dismiss"), style: .Cancel) { [weak self] (action) in - self?.resetGame() - } - alertController.addAction(cancelAction) - - self.presentViewController(alertController, animated: true) { } - } - - - + + // MARK: Properties + + @IBOutlet weak var collectionView: UICollectionView! + @IBOutlet weak var timerLabel: UILabel! + @IBOutlet weak var playButton: UIButton! + + let gameController = MemoryGame() + var timer:Timer? + + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + + gameController.delegate = self + resetGame() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if gameController.isPlaying { + resetGame() + } + } + + // MARK: - Methods + + func resetGame() { + gameController.stopGame() + if timer?.isValid == true { + timer?.invalidate() + timer = nil + } + collectionView.isUserInteractionEnabled = false + collectionView.reloadData() + timerLabel.text = String(format: "%@: ---", NSLocalizedString("TIME", comment: "time")) + playButton.setTitle(NSLocalizedString("Play", comment: "play"), for: .normal) + } + + @IBAction func didPressPlayButton(_ sender: UIButton) { + if gameController.isPlaying { + resetGame() + playButton.setTitle(NSLocalizedString("Play", comment: "play"), for: .normal) + } else { + setupNewGame() + playButton.setTitle(NSLocalizedString("Stop", comment: "stop"), for: .normal) + } + } + + func setupNewGame() { + let cardsData:[UIImage] = MemoryGame.defaultCardImages + gameController.newGame(cardsData: cardsData) + } + + @objc func gameTimerAction() { + timerLabel.text = String(format: "%@: %.0fs", NSLocalizedString("TIME", comment: "time"), gameController.elapsedTime) + } + + func savePlayerScore(name: String, score: TimeInterval) { + Highscores.sharedInstance.saveHighscore(name: name, score: score) + } + + // MARK: - UICollectionViewDataSource + + func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return gameController.numberOfCards > 0 ? gameController.numberOfCards : 12 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cardCell", for: indexPath as IndexPath) as! CardCVC + cell.showCard(show: false, animted: false) + + guard let card = gameController.cardAtIndex(index: indexPath.item) else { return cell } + cell.card = card + + return cell + } + + // MARK: UICollectionViewDelegate + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let cell = collectionView.cellForItem(at: indexPath as IndexPath) as! CardCVC + + if cell.shown { return } + gameController.didSelectCard(card: cell.card) + + collectionView.deselectItem(at: indexPath as IndexPath, animated:true) + } + + // MARK: - UICollectionViewDataSource + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + //let numberOfColumns:Int = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) + + let itemWidth: CGFloat = collectionView.frame.width / 3.0 - 15.0 //numberOfColumns as CGFloat - 10 //- (minimumInteritemSpacing * numberOfColumns)) + + return CGSize(width: itemWidth, height: itemWidth) + } + + // MARK: - MemoryGameDelegate + + func memoryGameDidStart(game: MemoryGame) { + collectionView.reloadData() + collectionView.isUserInteractionEnabled = true + + timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(gameTimerAction), userInfo: nil, repeats: true) + } + + func memoryGame(game: MemoryGame, showCards cards: [Card]) { + for card in cards { + guard let index = gameController.indexForCard(card: card) else { continue } + let cell = collectionView.cellForItem(at: NSIndexPath(item: index, section:0) as IndexPath) as! CardCVC + cell.showCard(show: true, animted: true) + } + } + + func memoryGame(game: MemoryGame, hideCards cards: [Card]) { + for card in cards { + guard let index = gameController.indexForCard(card: card) else { continue } + let cell = collectionView.cellForItem(at: NSIndexPath(item: index, section:0) as IndexPath) as! CardCVC + cell.showCard(show: false, animted: true) + } + } + + + func memoryGameDidEnd(game: MemoryGame, elapsedTime: TimeInterval) { + timer?.invalidate() + + let elapsedTime = gameController.elapsedTime + + let alertController = UIAlertController( + title: NSLocalizedString("Hurrah!", comment: "title"), + message: String(format: "%@ %.0f seconds", NSLocalizedString("You finished the game in", comment: "message"), elapsedTime), + preferredStyle: .alert) + + let saveScoreAction = UIAlertAction(title: NSLocalizedString("Save Score", comment: "save score"), style: .default) { [weak self] (_) in + let nameTextField = alertController.textFields![0] as UITextField + guard let name = nameTextField.text else { return } + self?.savePlayerScore(name: name, score: elapsedTime) + self?.resetGame() + } + + saveScoreAction.isEnabled = false + alertController.addAction(saveScoreAction) + + alertController.addTextField { (textField) in + textField.placeholder = NSLocalizedString("Your name", comment: "your name") + + NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, + object: textField, + queue: OperationQueue.main) { (notification) in + saveScoreAction.isEnabled = textField.text != "" + } + } + + let cancelAction = UIAlertAction(title: NSLocalizedString("Dismiss", comment: "dismiss"), style: .cancel) { [weak self] (action) in + self?.resetGame() + } + alertController.addAction(cancelAction) + + self.present(alertController, animated: true) { } + } } diff --git a/MemoryGame/MemoryGame/Controller/ScoresTableViewController.swift b/MemoryGame/MemoryGame/Controller/ScoresTableViewController.swift index fe6e83d..feffc96 100644 --- a/MemoryGame/MemoryGame/Controller/ScoresTableViewController.swift +++ b/MemoryGame/MemoryGame/Controller/ScoresTableViewController.swift @@ -9,63 +9,61 @@ import UIKit class ScoresTableViewController: UITableViewController { - - // MARK: - Properties - - var scores: Array> = [] - - // MARK: - Lifecycle - - override func viewDidLoad() { - super.viewDidLoad() - - scores = Highscores.sharedInstance.getHighscores() - tableView.reloadData() - } - - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - navigationController?.setNavigationBarHidden(false, animated: true) - } + + // MARK: - Properties + + var scores: Array> = [] + + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() - override func viewWillDisappear(animated: Bool) { - super.viewWillDisappear(animated) - navigationController?.setNavigationBarHidden(true, animated: true) - } + scores = Highscores.sharedInstance.getHighscores() + tableView.reloadData() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(false, animated: true) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(true, animated: true) + } + + // MARK: - UITableViewDataSource + + override func numberOfSections(in tableView: UITableView) -> Int { + return scores.count == 0 ? 0 : 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return scores.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "scoreCell", for: indexPath as IndexPath) - // MARK: - UITableViewDataSource - - override func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return scores.count == 0 ? 0 : 1 - } - - override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return scores.count - } + // Configure the cell... + let score = scores[indexPath.row] + let name = score["name"]! + let time = Double(score["score"]!) - override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCellWithIdentifier("scoreCell", forIndexPath: indexPath) - - // Configure the cell... - let score = scores[indexPath.row] - let name = score["name"]! - let time = Double(score["score"]!) - - cell.textLabel?.text = String(format: "%d. %@", indexPath.row+1, name) - cell.detailTextLabel?.text = String(format: "%.0fs", time!) - cell.detailTextLabel?.hidden = true - - return cell - } - - // MARK: - UITableViewDelegate + cell.textLabel?.text = String(format: "%d. %@", indexPath.row+1, name) + cell.detailTextLabel?.text = String(format: "%.0fs", time!) + cell.detailTextLabel?.isHidden = true - override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - tableView.deselectRowAtIndexPath(indexPath, animated: true) - - let cell = tableView.cellForRowAtIndexPath(indexPath) - cell?.detailTextLabel?.hidden = false - } - - + return cell + } + + // MARK: - UITableViewDelegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath as IndexPath, animated: true) + + let cell = tableView.cellForRow(at: indexPath as IndexPath) + cell?.detailTextLabel?.isHidden = false + } } diff --git a/MemoryGame/MemoryGame/Extensions/Array+Shuffle.swift b/MemoryGame/MemoryGame/Extensions/Array+Shuffle.swift index 9061c96..49d178d 100644 --- a/MemoryGame/MemoryGame/Extensions/Array+Shuffle.swift +++ b/MemoryGame/MemoryGame/Extensions/Array+Shuffle.swift @@ -9,10 +9,12 @@ import Foundation extension Array { - //Randomizes the order of the array elements - mutating func shuffle() { - for _ in 1...self.count { - self.sortInPlace { (_,_) in arc4random() < arc4random() } - } + //Randomizes the order of the array elements + mutating func shuffle() { + for _ in 1...self.count { + self.sort { (a, b) -> Bool in + return arc4random() < arc4random() + } } -} \ No newline at end of file + } +} diff --git a/MemoryGame/MemoryGame/Extensions/UIImage+Downloader.swift b/MemoryGame/MemoryGame/Extensions/UIImage+Downloader.swift index 0e06f2e..4307122 100644 --- a/MemoryGame/MemoryGame/Extensions/UIImage+Downloader.swift +++ b/MemoryGame/MemoryGame/Extensions/UIImage+Downloader.swift @@ -12,20 +12,20 @@ import UIKit.UIImage extension UIImage { - static func downloadImage(url: NSURL, completion: ((UIImage?) -> Void)?) { - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { - var image: UIImage? = nil - - defer { - dispatch_async(dispatch_get_main_queue()) { - completion?(image) - } - } - - if let data = NSData(contentsOfURL: url) { - image = UIImage(data: data) - } + static func downloadImage(url: NSURL, completion: ((UIImage?) -> Void)?) { + + DispatchQueue.global().async { + var image: UIImage? = nil + + defer { + DispatchQueue.main.async { + completion?(image) } + } + + if let data = NSData(contentsOf: url as URL) { + image = UIImage(data: data as Data) + } } -} \ No newline at end of file + } +} diff --git a/MemoryGame/MemoryGame/Model/Card.swift b/MemoryGame/MemoryGame/Model/Card.swift index b00f53c..5da9f7c 100644 --- a/MemoryGame/MemoryGame/Model/Card.swift +++ b/MemoryGame/MemoryGame/Model/Card.swift @@ -32,10 +32,10 @@ class Card : CustomStringConvertible { // MARK: - Methods var description: String { - return "\(id.UUIDString)" + return "\(id.uuidString)" } func equals(card: Card) -> Bool { return card.id.isEqual(id) } -} \ No newline at end of file +} diff --git a/MemoryGame/MemoryGame/Model/Highscores.swift b/MemoryGame/MemoryGame/Model/Highscores.swift index 1bdf2b3..129fcab 100644 --- a/MemoryGame/MemoryGame/Model/Highscores.swift +++ b/MemoryGame/MemoryGame/Model/Highscores.swift @@ -11,33 +11,33 @@ import Foundation let kHighscoresUserDefaultsKey = "Highscores" class Highscores { - static let sharedInstance = Highscores() + static let sharedInstance = Highscores() + + func getHighscores() -> [Dictionary] { + return userDefaults().array(forKey: kHighscoresUserDefaultsKey) as? [Dictionary] ?? [] + } + + func saveHighscore(name: String, score: TimeInterval) { + let entry = ["name": name, "score": String(score)] - func getHighscores() -> [Dictionary] { - return userDefaults().arrayForKey(kHighscoresUserDefaultsKey) as? [Dictionary] ?? [] - } + var scores: [Dictionary] = getHighscores() + scores.append(entry) - func saveHighscore(name: String, score: NSTimeInterval) { - let entry = ["name": name, "score": String(score)] - - var scores = getHighscores() - scores.append(entry) - - let sortedScores = scores.sort { (entry1, entry2) -> Bool in - let n1:String = entry1["score"]! - let n2:String = entry2["score"]! - - return Double(n1) < Double(n2) - } - - if scores.count > 10 { - scores.removeLast() - } - - userDefaults().setObject(sortedScores, forKey: kHighscoresUserDefaultsKey) + let sortedScores = scores.sorted { (entry1, entry2) -> Bool in + let n1:String = entry1["score"]! + let n2:String = entry2["score"]! + + return Double(n1)! < Double(n2)! } - private func userDefaults() -> NSUserDefaults { - return NSUserDefaults.standardUserDefaults() + if scores.count > 10 { + scores.removeLast() } -} \ No newline at end of file + + userDefaults().set(sortedScores, forKey: kHighscoresUserDefaultsKey) + } + + private func userDefaults() -> UserDefaults { + return UserDefaults.standard + } +} diff --git a/MemoryGame/MemoryGame/Model/MemoryGame.swift b/MemoryGame/MemoryGame/Model/MemoryGame.swift index 2754c07..fddad49 100644 --- a/MemoryGame/MemoryGame/Model/MemoryGame.swift +++ b/MemoryGame/MemoryGame/Model/MemoryGame.swift @@ -12,130 +12,129 @@ import UIKit.UIImage // MARK: - MemoryGameDelegate protocol MemoryGameDelegate { - func memoryGameDidStart(game: MemoryGame) - func memoryGame(game: MemoryGame, showCards cards: [Card]) - func memoryGame(game: MemoryGame, hideCards cards: [Card]) - func memoryGameDidEnd(game: MemoryGame, elapsedTime: NSTimeInterval) + func memoryGameDidStart(game: MemoryGame) + func memoryGame(game: MemoryGame, showCards cards: [Card]) + func memoryGame(game: MemoryGame, hideCards cards: [Card]) + func memoryGameDidEnd(game: MemoryGame, elapsedTime: TimeInterval) } // MARK: - MemoryGame class MemoryGame { - - // MARK: - Properties - - static var defaultCardImages:[UIImage] = [ - UIImage(named: "brand1")!, - UIImage(named: "brand2")!, - UIImage(named: "brand3")!, - UIImage(named: "brand4")!, - UIImage(named: "brand5")!, - UIImage(named: "brand6")! - ]; - - var cards:[Card] = [Card]() - var delegate: MemoryGameDelegate? - var isPlaying: Bool = false - - private var cardsShown:[Card] = [Card]() - private var startTime:NSDate? - - var numberOfCards: Int { - get { - return cards.count - } + + // MARK: - Properties + + static var defaultCardImages:[UIImage] = [ + UIImage(named: "brand1")!, + UIImage(named: "brand2")!, + UIImage(named: "brand3")!, + UIImage(named: "brand4")!, + UIImage(named: "brand5")!, + UIImage(named: "brand6")! + ]; + + var cards:[Card] = [Card]() + var delegate: MemoryGameDelegate? + var isPlaying: Bool = false + + private var cardsShown:[Card] = [Card]() + private var startTime:NSDate? + + var numberOfCards: Int { + get { + return cards.count } - - var elapsedTime : NSTimeInterval { - get { - guard startTime != nil else { - return -1 - } - return NSDate().timeIntervalSinceDate(startTime!) - } + } + + var elapsedTime : TimeInterval { + get { + guard startTime != nil else { + return -1 + } + return NSDate().timeIntervalSince(startTime! as Date) } + } + + // MARK: - Methods + + func newGame(cardsData:[UIImage]) { + cards = randomCards(cardsData: cardsData) + startTime = NSDate.init() + isPlaying = true + delegate?.memoryGameDidStart(game: self) + } + + func stopGame() { + isPlaying = false + cards.removeAll() + cardsShown.removeAll() + startTime = nil + } + + func didSelectCard(card: Card?) { + guard let card = card else { return } - // MARK: - Methods - - func newGame(cardsData:[UIImage]) { - cards = randomCards(cardsData) - startTime = NSDate.init() - isPlaying = true - delegate?.memoryGameDidStart(self) - } - - func stopGame() { - isPlaying = false - cards.removeAll() - cardsShown.removeAll() - startTime = nil - } + delegate?.memoryGame(game: self, showCards: [card]) - func didSelectCard(card: Card?) { - guard let card = card else { return } + if unpairedCardShown() { + let unpaired = unpairedCard()! + if card.equals(card: unpaired) { + cardsShown.append(card) + } else { + let unpairedCard = cardsShown.removeLast() - delegate?.memoryGame(self, showCards: [card]) - - if unpairedCardShown() { - let unpaired = unpairedCard()! - if card.equals(unpaired) { - cardsShown.append(card) - } else { - let unpairedCard = cardsShown.removeLast() - - let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))) - dispatch_after(delayTime, dispatch_get_main_queue()) { - self.delegate?.memoryGame(self, hideCards:[card, unpairedCard]) - } - } - } else { - cardsShown.append(card) - } - - if cardsShown.count == cards.count { - finishGame() + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self.delegate?.memoryGame(game: self, hideCards: [card, unpairedCard]) } + } + } else { + cardsShown.append(card) } - func cardAtIndex(index: Int) -> Card? { - if cards.count > index { - return cards[index] - } else { - return nil - } + if cardsShown.count == cards.count { + finishGame() } - - func indexForCard(card: Card) -> Int? { - for index in 0...cards.count-1 { - if card === cards[index] { - return index - } - } - return nil + } + + func cardAtIndex(index: Int) -> Card? { + if cards.count > index { + return cards[index] + } else { + return nil } - - private func finishGame() { - isPlaying = false - delegate?.memoryGameDidEnd(self, elapsedTime: elapsedTime) + } + + func indexForCard(card: Card) -> Int? { + for index in 0...cards.count-1 { + if card === cards[index] { + return index + } } - - private func unpairedCardShown() -> Bool { - return cardsShown.count % 2 != 0 + return nil + } + + private func finishGame() { + isPlaying = false + delegate?.memoryGameDidEnd(game: self, elapsedTime: elapsedTime) + } + + private func unpairedCardShown() -> Bool { + return cardsShown.count % 2 != 0 + } + + private func unpairedCard() -> Card? { + let unpairedCard = cardsShown.last + return unpairedCard + } + + private func randomCards(cardsData:[UIImage]) -> [Card] { + var cards = [Card]() + for i in 0...cardsData.count-1 { + let card = Card.init(image: cardsData[i]) + cards.append(contentsOf: [card, Card.init(card: card)]) } - - private func unpairedCard() -> Card? { - let unpairedCard = cardsShown.last - return unpairedCard - } - - private func randomCards(cardsData:[UIImage]) -> [Card] { - var cards = [Card]() - for i in 0...cardsData.count-1 { - let card = Card.init(image: cardsData[i]) - cards.appendContentsOf([card, Card.init(card: card)]) - } - cards.shuffle() - return cards - } - -} \ No newline at end of file + cards.shuffle() + return cards + } + +} diff --git a/MemoryGame/MemoryGame/View/CardCVC.swift b/MemoryGame/MemoryGame/View/CardCVC.swift index 936d4f5..96ed048 100644 --- a/MemoryGame/MemoryGame/View/CardCVC.swift +++ b/MemoryGame/MemoryGame/View/CardCVC.swift @@ -9,54 +9,54 @@ import UIKit.UICollectionViewCell class CardCVC: UICollectionViewCell { - - // MARK: - Properties - - @IBOutlet weak var frontImageView: UIImageView! - @IBOutlet weak var backImageView: UIImageView! - - var card:Card? { - didSet { - guard let card = card else { return } - frontImageView.image = card.image - } + + // MARK: - Properties + + @IBOutlet weak var frontImageView: UIImageView! + @IBOutlet weak var backImageView: UIImageView! + + var card:Card? { + didSet { + guard let card = card else { return } + frontImageView.image = card.image } + } + + private(set) var shown: Bool = false + + // MARK: - Methods + + func showCard(show: Bool, animted: Bool) { + frontImageView.isHidden = false + backImageView.isHidden = false + shown = show - private(set) var shown: Bool = false - - // MARK: - Methods - - func showCard(show: Bool, animted: Bool) { - frontImageView.hidden = false - backImageView.hidden = false - shown = show - - if animted { - if show { - UIView.transitionFromView(backImageView, - toView: frontImageView, - duration: 0.5, - options: [.TransitionFlipFromRight, .ShowHideTransitionViews], - completion: { (finished: Bool) -> () in - }) - } else { - UIView.transitionFromView(frontImageView, - toView: backImageView, - duration: 0.5, - options: [.TransitionFlipFromRight, .ShowHideTransitionViews], - completion: { (finished: Bool) -> () in - }) - } - } else { - if show { - bringSubviewToFront(frontImageView) - backImageView.hidden = true - } else { - bringSubviewToFront(backImageView) - frontImageView.hidden = true - } - } + if animted { + if show { + UIView.transition(from: backImageView, + to: frontImageView, + duration: 0.5, + options: [.transitionFlipFromRight, .showHideTransitionViews], + completion: { (finished: Bool) -> () in + }) + } else { + UIView.transition(from: frontImageView, + to: backImageView, + duration: 0.5, + options: [.transitionFlipFromRight, .showHideTransitionViews], + completion: { (finished: Bool) -> () in + }) + } + } else { + if show { + bringSubview(toFront: frontImageView) + backImageView.isHidden = true + } else { + bringSubview(toFront: backImageView) + frontImageView.isHidden = true + } } - - + } + + } From ac2c2203d157a184a9878ce074b0413dec98e3db Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 12 Jul 2018 11:32:47 +0900 Subject: [PATCH 2/5] Set app configure to run on both iPhone and iPad --- MemoryGame/MemoryGame.xcodeproj/project.pbxproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MemoryGame/MemoryGame.xcodeproj/project.pbxproj b/MemoryGame/MemoryGame.xcodeproj/project.pbxproj index 73df885..fcb9ba8 100644 --- a/MemoryGame/MemoryGame.xcodeproj/project.pbxproj +++ b/MemoryGame/MemoryGame.xcodeproj/project.pbxproj @@ -152,6 +152,7 @@ TargetAttributes = { 31623E3C1CA5E40400695940 = { CreatedOnToolsVersion = 7.2.1; + DevelopmentTeam = 4V6HDFWSR4; }; }; }; @@ -312,6 +313,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 4V6HDFWSR4; INFOPLIST_FILE = MemoryGame/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -319,6 +321,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -327,12 +330,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = 4V6HDFWSR4; INFOPLIST_FILE = MemoryGame/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.danielts.MemoryGame; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; From e6161d09943a87c0200a3f1f2b0733f3f9d81d35 Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 12 Jul 2018 11:33:15 +0900 Subject: [PATCH 3/5] Fix CardChange popup to work on iPad --- .../Controller/CustomizeCardsTableViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift index a827548..e2cd74d 100644 --- a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift +++ b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift @@ -34,9 +34,11 @@ class CustomizeCardsTableViewController: UITableViewController, UIImagePickerCon // MARK: - Methods - private func didPressChangeCard(index: Int) { + private func didPressChangeCard(index: Int, sender: UIView?) { let alertController = UIAlertController(title: NSLocalizedString("Select card image source:", comment: "-"), message: nil, preferredStyle: .actionSheet) + alertController.popoverPresentationController?.sourceView = sender + if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { let PhotoLibraryAction = UIAlertAction(title: NSLocalizedString("Photo Library", comment: "pl"), style: .default) { [weak self] (action) in let imagePicker = UIImagePickerController() @@ -138,9 +140,9 @@ class CustomizeCardsTableViewController: UITableViewController, UIImagePickerCon override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath as IndexPath, animated: true) - + let imageView = tableView.cellForRow(at: indexPath)?.contentView.viewWithTag(2) as! UIImageView selectedIndexPath = indexPath - didPressChangeCard(index: indexPath.row) + didPressChangeCard(index: indexPath.row, sender: imageView) } // MARK: - UIImagePickerControllerDelegate From 0e58f286a379252651aa2babc658da76948c5884 Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 12 Jul 2018 11:38:31 +0900 Subject: [PATCH 4/5] Fix camera permission crash by adding CameraUsageDescription --- MemoryGame/MemoryGame/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MemoryGame/MemoryGame/Info.plist b/MemoryGame/MemoryGame/Info.plist index 9821f9b..3ef9c31 100644 --- a/MemoryGame/MemoryGame/Info.plist +++ b/MemoryGame/MemoryGame/Info.plist @@ -40,5 +40,7 @@ UIViewControllerBasedStatusBarAppearance + NSCameraUsageDescription + $(PRODUCT_NAME) uses camera to take a picture for card image change function From 60954b663762358e4bbdf6e62ae434adf44f15c3 Mon Sep 17 00:00:00 2001 From: Quang Nguyen Date: Thu, 12 Jul 2018 11:45:02 +0900 Subject: [PATCH 5/5] Update ImagePickerController delegate --- .../Controller/CustomizeCardsTableViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift index e2cd74d..cc33f5b 100644 --- a/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift +++ b/MemoryGame/MemoryGame/Controller/CustomizeCardsTableViewController.swift @@ -147,7 +147,7 @@ class CustomizeCardsTableViewController: UITableViewController, UIImagePickerCon // MARK: - UIImagePickerControllerDelegate - private func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { picker.dismiss(animated: true, completion: nil) guard let selectedIndexPath = selectedIndexPath else { return }