I am creating an app for a project that determines whether or not a year is a leap year or not.
The overall logic itself is very simple, however I am trying to make it so when the button is pressed, it will take the value within the text field and then update var year with that value.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var guessLabel: UILabel!
#IBOutlet weak var guessTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func logic() {
// var year = guessTextField This is where the issue is.
var value1 = (year % 4)
var value2 = (year % 100)
var value3 = (year % 400)
var criteria: Bool!
print (value1)
print (value2)
criteria = false
func yearChecker() {
if value1 == 0 {
criteria = true
}
if value2 != 0, value1 == 0 {
criteria = true
}
if value3 == 0, value1 == 0 {
criteria = true
}
if criteria == true {
leapYearAlert()
}
if criteria == false {
showBoundsAlert()
}
}
}
#IBAction func submitButton(_ sender: UIButton) {
var year = guessTextField.text
logic()
}
func showBoundsAlert() {
let alert = UIAlertController(title: "Sorry, The Year You Chose is Not Special", message: "You're Year is Not a Leap Year:(", preferredStyle: .alert)
let action = UIAlertAction(title: "Okay", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
func leapYearAlert() {
let alert = UIAlertController( title: "Your Year is Special!", message: "The Year You Chose is in Fact a Leap Year!", preferredStyle: .alert)
let action = UIAlertAction(title: "Check For Another Year?", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}
Cast year as Int because guessTextField.text return String.
#IBAction func submitButton(_ sender: UIButton) {
logic()
}
Change value1, value2, value3 from var to let as they don't change.
func logic() {
let year: Int = Int(guessTextField.text ?? "0")!
let value1: Int = (year % 4)
let value2: Int = (year % 100)
let value3: Int = (year % 400)
var criteria: Bool = false
print (value1)
print (value2)
if value1 == 0 {
criteria = true
}
if value2 != 0, value1 == 0 {
criteria = true
}
if value3 == 0, value1 == 0 {
criteria = true
}
if criteria == true {
leapYearAlert()
} else {
showBoundsAlert()
}
}
Related
This code has all my labels Im trying to use. I can't save the High score and implement it into the game. Now its saying I need to type more so i'm just going to keep typing until it tells me i'm good. It still hasn't told me i'm goo i'm actually very surprised wow.
import UIKit
import CoreData
import SpriteKit
var timer:Timer?
var seconds:Int = 5
var maxSeconds: Int = 5
var totalPoints:Int = 0
var high:Int = 0
let userDefaults = UserDefaults.standard
let defaults = UserDefaults.standard
class ViewController: UIViewController {
#IBOutlet weak var menu: UIButton!
var i = 0
var point = 0
#IBOutlet weak var highScore: UILabel!
#IBOutlet weak var timeLabel:UILabel?
#IBOutlet weak var points:UILabel?
#IBOutlet weak var totalPoint: UILabel!
#objc func tapped(){
i += 1
switch i {
case 1:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
case 2:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
case 3:
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.warning)
case 4:
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()
case 5:
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
case 6:
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
default:
let generator = UISelectionFeedbackGenerator()
generator.selectionChanged()
i = 0
}
}
func updateTimeLabel()
{
if(timeLabel != nil)
{
let sec:Int = seconds % 30
let sec_p:String = String(format: "%02d", sec)
timeLabel!.text = "\(sec_p)"
}
}
#objc func onUpdateTimer() -> Void
{
if(seconds > 0 && seconds <= maxSeconds)
{
seconds -= 1
updateTimeLabel()
}
else if(seconds == 0)
{
if(timer != nil)
{
timer!.invalidate()
timer = nil
userDefaults.set(totalPoints, forKey: "totalPoints")
let alertController = UIAlertController(title: "Time Up!", message: "Your time is up! You got a score of \(point) points and your total coins now is \(totalPoints). You Can Do Better", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Play Again!", style: .default, handler: nil)
alertController.addAction(restartAction)
let FirstSubview = alertController.view.subviews.first
let AlertContentView = FirstSubview?.subviews.first
for subview in (AlertContentView?.subviews)! {
subview.backgroundColor = UIColor(red: 226/255.0, green: 158/255.0, blue: 152/255.0, alpha: 5.0)
subview.layer.cornerRadius = 1
subview.alpha = 1
}
self.present(alertController, animated: true, completion: nil)
point = 0
seconds = maxSeconds
updateTimeLabel()
menu.isHidden = false
defaults.set(high, forKey: "high")
}
}
}
#IBAction func Restart(_ sender: Any) {
}
#IBAction func adder(_ sender: Any)
{
point += 1
points?.text = "\(point)"
if point % 10 == 0 {
totalPoints = 10 + totalPoints
totalPoint?.text = String(totalPoints)
}
if(timer == nil)
{
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector:#selector(onUpdateTimer), userInfo:nil, repeats:true)
}
tapped()
menu.isHidden = true
}
override func viewDidLoad() {
points?.text = "\(point)"
let total = userDefaults.integer(forKey: "totalPoints")
if total != 0 {
totalPoints = total
} else {
totalPoints = 0
}
let score = defaults.integer(forKey: "high")
if high < point {
high = score
} else {
high = 0
}
totalPoint?.text = String(totalPoints)
updateTimeLabel()
highScore.text = String(high)
}
}
Do I need to put something at the end? Well it looks like that didn't work either!
#for example
func saveHighScore() {
UserDefaults.standard.set(score, forKey: "HIGHSCORE")
}
I have a page based app, using RootViewController, ModelViewController, DataViewController, and a SearchViewController.
In my searchViewController, I search for an item and then add or remove that Item to an array which is contained in a Manager class(and UserDefaults), which the modelViewController uses to instantiate an instance of DataViewController with the correct information loaded using the dataObject. Depending on whether an Item was added or removed, I use a Bool to determine which segue was used, addCoin or removeCoin, so that the RootViewController(PageView) will show either the last page in the array, (when a page is added) or the first (when removed).
Everything was working fine until I ran into an error which I can not diagnose, the problem is that when I add a page, the app crashes, giving me a "unexpectadely found nil when unwrapping an optional value"
This appears to be the problem function, in the searchViewController 'self.performSegue(withIdentifier: "addCoin"' seems to be called instantly, even without the dispatchque:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
Meaning that In my DataViewController, this function will find nil:
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
I can't find out why it does not wait for completion.
I also get this error about threads:
[Assert] Cannot be called with asCopy = NO on non-main thread.
which is why I try to do the push segue using dispatch que
Here is my searchViewController full code:
import UIKit
class SearchViewController: UIViewController, UISearchBarDelegate {
let selectionLabel = UILabel()
let searchBar = UISearchBar()
let addButton = UIButton()
let removeButton = UIButton()
var filteredObject: [String] = []
var dataObject = ""
var isSearching = false
//Add Button Action.
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
//Remove button action.
#objc func removeButtonActon(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.removeCoin(coin: dataObject)
self.performSegue(withIdentifier: "addCoin", sender: self)
}
searchBar.text = ""
}
//Prepare for segue, pass removeCoinSegue Bool depending on remove or addCoin.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = true
}
} else if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = false
}
}
}
//Remove button action.
#objc func removeButtonAction(sender: UIButton!) {
if Manager.shared.coins.count == 1 {
removeAlert()
} else {
Manager.shared.removeCoin(coin: dataObject)
print(Manager.shared.coins)
print(dataObject)
searchBar.text = ""
self.removeButton.isHidden = true
DispatchQueue.main.async {
self.performSegue(withIdentifier: "removeCoin", sender: self)
}
}
}
//Search/Filter the struct from CGNames, display both the Symbol and the Name but use the ID as dataObject.
func filterStructForSearchText(searchText: String, scope: String = "All") {
if !searchText.isEmpty {
isSearching = true
filteredObject = CGNames.shared.coinNameData.filter {
// if you need to search key and value and include partial matches
// $0.key.contains(searchText) || $0.value.contains(searchText)
// if you need to search caseInsensitively key and value and include partial matches
$0.name.range(of: searchText, options: .caseInsensitive) != nil || $0.symbol.range(of: searchText, options: .caseInsensitive) != nil
}
.map{ $0.id }
} else {
isSearching = false
print("NoText")
}
}
//Running filter function when text changes.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterStructForSearchText(searchText: searchText)
if isSearching == true && filteredObject.count > 0 {
addButton.isHidden = false
dataObject = filteredObject[0]
selectionLabel.text = dataObject
if Manager.shared.coins.contains(dataObject) {
removeButton.isHidden = false
addButton.isHidden = true
} else {
removeButton.isHidden = true
addButton.isHidden = false
}
} else {
addButton.isHidden = true
removeButton.isHidden = true
selectionLabel.text = "e.g. btc/bitcoin"
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the UI.
self.view.backgroundColor = .gray
setupView()
}
override func viewDidLayoutSubviews() {
}
//Hide keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//Alerts
func removeAlert() {
let alertController = UIAlertController(title: "Can't Remove", message: "\(dataObject) can't be deleted, add another to delete \(dataObject)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func Duplicate() {
let alertController = UIAlertController(title: "Duplicate", message: "\(dataObject) is already in your pages!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func max() {
let alertController = UIAlertController(title: "Maximum Reached", message: "\(dataObject) can't be added, you have reached the maximum of 5 coins. Please delete a coin to add another.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
and here is the DataViewController
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
//Variables and Objects.
//The dataObject carries the chosen cryptocurrencies ID from the CoinGecko API to use to get the correct data to load on each object.
var dataObject = String()
//The DefaultCurrency (gbp, eur...) chosen by the user.
var defaultCurrency = ""
//The Currency Unit taken from the exchange section of the API.
var currencyUnit = CGExchange.shared.exchangeData[0].rates.gbp.unit
var secondaryUnit = CGExchange.shared.exchangeData[0].rates.eur.unit
var tertiaryUnit = CGExchange.shared.exchangeData[0].rates.usd.unit
//Index of the dataObject
var dataIndex = Int()
//Objects
let cryptoLabel = UILabel()
let cryptoIconImage = UIImageView()
let secondaryPriceLabel = UILabel()
let mainPriceLabel = UILabel()
let tertiaryPriceLabel = UILabel()
//Custom Fonts.
let customFont = UIFont(name: "AvenirNext-Heavy", size: UIFont.labelFontSize)
let secondFont = UIFont(name: "AvenirNext-BoldItalic" , size: UIFont.labelFontSize)
//Setup Functions
//Get the index of the dataObject
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
//Label
func setupLabels() {
//cryptoLabel from dataObject as name.
cryptoLabel.text = CGPrices.shared.coinData[dataIndex].name
//Prices from btc Exchange rate.
let btcPrice = CGPrices.shared.coinData[dataIndex].current_price!
let dcExchangeRate = CGExchange.shared.exchangeData[0].rates.gbp.value
let secondaryExchangeRate = CGExchange.shared.exchangeData[0].rates.eur.value
let tertiaryExchangeRate = CGExchange.shared.exchangeData[0].rates.usd.value
let realPrice = (btcPrice * dcExchangeRate)
let secondaryPrice = (btcPrice * secondaryExchangeRate)
let tertiaryPrice = (btcPrice * tertiaryExchangeRate)
secondaryPriceLabel.text = "\(secondaryUnit)\(String((round(1000 * secondaryPrice) / 1000)))"
mainPriceLabel.text = "\(currencyUnit)\(String((round(1000 * realPrice) /1000)))"
tertiaryPriceLabel.text = "\(tertiaryUnit)\(String((round(1000 * tertiaryPrice) / 1000)))"
}
//Image
func getIcon() {
let chosenImage = CGPrices.shared.coinData[dataIndex].image
let remoteImageUrl = URL(string: chosenImage)
guard let url = remoteImageUrl else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
DispatchQueue.main.async {
self.cryptoIconImage.image = UIImage(data: data)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// for family in UIFont.familyNames.sorted() {
// let names = UIFont.fontNames(forFamilyName: family)
// print("Family: \(family) Font names: \(names)")
// }
// Do any additional setup after loading the view, typically from a nib.
self.setupLayout()
self.getIndex()
self.setupLabels()
self.getIcon()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
view.backgroundColor = .lightGray
}
}
Edit: CGPrices Class with getData method:
import Foundation
class CGPrices {
struct Coins: Decodable {
let id: String
let name: String
let symbol: String
let image: String
let current_price: Double?
let low_24h: Double?
//let price_change_24h: Double?
}
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i + 1) < coins.count { coinsEncoded += "%2C" }
}
print("encoded")
}
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let coinsData = try JSONDecoder().decode([Coins].self, from: data)
self.coinData = coinsData
completion(arr)
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
print(data)
}
}.resume()
}
func refresh(completion: () -> ()) {
defaultCurrency = UserDefaults.standard.string(forKey: "DefaultCurrency")!
completion()
}
}
I figured it out.
The problem was inside my getData method I was not updated the coins array:
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i+1)<coins.count { coinsEncoded+="%2C" }
}
print("encoded")
}
I needed to add this line in getData:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
//Adding this line to update the array so that the URL is appended correctly.
coins = Manager.shared.coins
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
This would fix the finding nil in the DataViewController, but the app would still crash do to updating UI Elements on a background thread, as the segue was called inside the completion handler of the getData method. to fix this, I used DispatchQue.Main.Async on the segue inside the getData method in the addButton function, to ensure that everything is updated on the main thread, like so:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
print("starting")
CGPrices.shared.getData(arr: true) { (arr) in
print("complete")
print(CGPrices.shared.coinData)
//Here making sure it is updated on main thread.
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
}
}
searchBar.text = ""
}
Thanks for all the comments as they helped me to figure this out, and I learned a lot in doing so. Hopefully this can help someone else in their thought process when debugging, as one can get so caught up in one area of a problem, and forget to take a step back and look to other areas.
I have a UIPickerView that is used to play the game Go Fish so data in it is continuously changing. AFter a few selections with the array I eventually get the error "Fatal Error: Index Out of Range" in the console and the error "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)" on another line. Any idea on what is causing this and how to fix it.
Code:
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
///Main Section///
//Game
#IBOutlet weak var GameCardsLeft: UILabel!
//CPU
#IBOutlet weak var CPUCardsLeft: UILabel!
#IBOutlet weak var CPUPairs: UILabel!
//Player
#IBOutlet weak var UserPairs: UILabel!
#IBOutlet weak var pickerView: UIPickerView!
//Variables
var deck = [Card]()
//Creates a deck of cards
func createDeck() {
var i = 0 //Suite
while(i < 2) {
var s = "" //suite
var x = 1 //Value
if(i == 0) {
s = "Clubs"
}/* else if(i == 1) {
s = "Spades"
} else if(i == 2) {
s = "Diamonds"
}*/ else {
s = "Hearts"
}
while(x < 14) {
let tempCard = Card()
tempCard.value = x
tempCard.suite = s
tempCard.assignRank(value: tempCard.value)
tempCard.setDescription(rank: tempCard.rank, suite: tempCard.suite)
x += 1
deck.append(tempCard)
}
i += 1
}
}
//Shuffles the deck
func shuffle() {
var tempDeck = [Card]()
while deck.count > 0 {
let random = Int(arc4random_uniform(UInt32(deck.count)))
let card = deck.remove(at: random)
tempDeck.append(card)
}
deck = tempDeck
}
///Go Fish Section///
var drawDeck = [Card]()
var playerDeck = [Card]()
var playerPairsArray = [Card]()
var cpuDeck = [Card]()
var cpuPairsArray = [Card]()
var selectedCard = Card()
var gameOver = false
var randomIndex = 0
func deal() {
for card in deck {
if(playerDeck.count < 5) {
playerDeck.append(card)
} else if(cpuDeck.count < 5) {
cpuDeck.append(card)
} else {
drawDeck.append(card)
}
}
}
//Player draw card
func playerDraw() {
if(playerDeck.count == 0) {
while(playerDeck.count != 7) {
if(drawDeck.count > 0) {
playerDeck.append(drawDeck[0])
drawDeck.remove(at: 0)
} else {
break
}
}
} else {
if(drawDeck.count > 0) {
playerDeck.append(drawDeck[0])
drawDeck.remove(at: 0)
}
}
}
//CPU draw card
func cpuDraw() {
if(cpuDeck.count == 0) {
while(cpuDeck.count != 7) {
if(drawDeck.count > 0) {
cpuDeck.append(drawDeck[0])
drawDeck.remove(at: 0)
} else {
break
}
}
} else {
if(drawDeck.count > 0) {
cpuDeck.append(drawDeck[0])
drawDeck.remove(at: 0)
}
}
}
//Player's turn check cpu hand
func checkCPUHand(selectedCard: Card) {
var x = 0
while(x < cpuDeck.count) {
if(selectedCard.value == cpuDeck[x].value) {
playerDeck.append(cpuDeck[x])
cpuDeck.remove(at: x)
checkForPlayerPairs()
return
} else {
x += 1
}
}
playerDraw()
}
//CPU's turn check player hand
func checkPlayerHand(selectedCard: Card) {
var x = 0
while(x < playerDeck.count) {
if(selectedCard.value == playerDeck[x].value) {
cpuDeck.append(playerDeck[x])
playerDeck.remove(at: x)
checkForCPUPairs()
return
} else {
x += 1
}
}
cpuDraw()
}
//Check for player Pairs
func checkForPlayerPairs() {
var i = 0
var k = 0
var pairFound = false
while(i < playerDeck.count) {
pairFound = false
k = i + 1
while(k < playerDeck.count) {
if(playerDeck[i].value == playerDeck[k].value) {
pairFound = true
break
} else {
k += 1
}
}
if(pairFound) {
playerPairsArray.append(playerDeck[i])
playerDeck.remove(at: k)
playerDeck.remove(at: i)
} else {
i += 1
}
}
}
//Check for cpu pairs
func checkForCPUPairs() {
var i = 0
var k = 0
var pairFound = false
while(i < cpuDeck.count) {
pairFound = false
k = i + 1
while(k < cpuDeck.count) {
if(cpuDeck[i].value == cpuDeck[k].value) {
pairFound = true
break
} else {
k += 1
}
}
if(pairFound) {
cpuPairsArray.append(cpuDeck[i])
cpuDeck.remove(at: k)
cpuDeck.remove(at: i)
} else {
i += 1
}
}
}
/* Start of PickerView Stuff */
//Returns the number of columns
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
//Returns the number of rows
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return playerDeck.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return playerDeck[row].description //Error is in this line(thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0))
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedCard = playerDeck[row]
//have var = pickerData[row]
}
/* End of PickerView Stuff */
func organizeCardDeck() {
playerDeck = playerDeck.sorted { $0.value < $1.value }
cpuDeck = cpuDeck.sorted { $0.value < $1.value }
}
func start() {
createDeck()
shuffle()
shuffle()
deal()
organizeCardDeck()
checkForPlayerPairs()
checkForCPUPairs()
GameCardsLeft.text = "\(drawDeck.count)"
CPUCardsLeft.text = "\(cpuDeck.count)"
CPUPairs.text = "\(cpuPairsArray.count)"
UserPairs.text = "\(playerPairsArray.count)"
}
func checkGameOver() {
if(cpuDeck.count == 0 && playerDeck.count == 0 && drawDeck.count == 0) {
let cpuPairCount = cpuPairsArray.count
let playerPairCount = playerPairsArray.count
playerDeck.removeAll()
cpuDeck.removeAll()
deck.removeAll()
cpuPairsArray.removeAll()
playerPairsArray.removeAll()
/*if(playerPairCount > cpuPairCount) {
let alertController = UIAlertController(title: "Game Over", message: "You win", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Restart", style: .default, handler: nil)
alertController.addAction(defaultAction)
} else if(playerPairCount < cpuPairCount) {
let alertController = UIAlertController(title: "Game Over", message: "You lose", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Restart", style: .default, handler: nil)
alertController.addAction(defaultAction)
} else if(playerPairCount == cpuPairCount) {
let alertController = UIAlertController(title: "Game Over", message: "Tie game", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Restart", style: .default, handler: nil)
alertController.addAction(defaultAction)
}*/
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.pickerView.delegate = self
self.pickerView.dataSource = self
/*var alert = UIAlertController(title: "Go FIsh Go", message: "", preferredStyle: .alert)
var startAlert = UIAlertAction(title: "Start", style: .default, handler: nil)
alert.addAction(startAlert)
present(alert, animated: true, completion: nil)*/
start()
}
#IBAction func SelectCard(_ sender: UIButton) {
checkCPUHand(selectedCard: selectedCard)
checkForPlayerPairs()
pickerView.reloadAllComponents()
pickerView.selectRow(0, inComponent: 0, animated: true)
if(playerDeck.count == 0 && drawDeck.count > 0) {
playerDraw()
}
GameCardsLeft.text = "\(drawDeck.count)"
CPUCardsLeft.text = "\(cpuDeck.count)"
CPUPairs.text = "\(cpuPairsArray.count)"
UserPairs.text = "\(playerPairsArray.count)"
randomIndex = Int(arc4random_uniform(UInt32(cpuDeck.count)))
checkPlayerHand(selectedCard: cpuDeck[randomIndex])
checkForCPUPairs()
if(cpuDeck.count == 0 && drawDeck.count > 0) {
cpuDraw()
}
GameCardsLeft.text = "\(drawDeck.count)"
CPUCardsLeft.text = "\(cpuDeck.count)"
CPUPairs.text = "\(cpuPairsArray.count)"
UserPairs.text = "\(playerPairsArray.count)"
checkGameOver()
}
}
Change return playerDeck[row].description to: return (row < playerDeck.count ? playerDeck[row].description : nil), and change selectedCard = playerDeck[row] to:
if row < playerDeck.count {
self.selectedCard = playerDeck[row]
}
Also, call self.pickerView.reloadAllComponents() right before returning from your deal, playerDraw, checkCPUHand, checkPlayerHand, checkForPlayerPairs, organizeCardDeck, and checkGameOver functions, and any other functions that modify playerDeck.
I am having problems with my interstitial ads popping up too often. The full screen ads comes on after every game over screen, meaning each time the player loses the ad pops up which i feel will get annoying. Is there a way to stop this happening after each failure to maybe after every 2 or 3 gameovers?
I have attached my code below, any help would be greatly appreciated.
import UIKit
import AVFoundation
import GoogleMobileAds
import Social
class ViewController: UIViewController {
#IBOutlet var bannerView: GADBannerView!
var admobInterstitial : GADInterstitial?
var timerAds : NSTimer?
var player:AVAudioPlayer = AVAudioPlayer()
var score = 0
var timer = NSTimer()
var seconds = 8
var watch = true
var watch1 = true
var gameActive = true
var hiScore = 0
var counter = 1
var turn = 0
var timer_anim = NSTimer()
#IBOutlet var btn: UIButton!
#IBOutlet var scr: UILabel!
#IBOutlet var scoreLabel: UILabel!
#IBOutlet var again: UIButton!
#IBOutlet var highScore: UILabel!
#IBOutlet var gameTimer: UILabel!
#IBOutlet var startView: UIImageView!
#IBOutlet var startButton: UIButton!
#IBOutlet var game_over: UIImageView!
#IBOutlet var twBtn: UIButton!
#IBAction func twitterBtn(sender: AnyObject) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter)
{
var twShare:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twShare.setInitialText("I dare you get a higher score than mine : ( \(score) ) GET it NOW on IOS : https://itunes.apple.com/us/app/oscar-cookie/id1099453391?mt=8")
twShare.addImage(UIImage(named: "start.png"))
self.presentViewController(twShare, animated: true, completion: nil)
}
else
{
var alert = UIAlertController(title: "Account", message: "Please login to Twitter to Tweet", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBOutlet var fbBtn: UIButton!
#IBAction func facebookBtn(sender: AnyObject) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook)
{
var fbShare:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
fbShare.setInitialText("I dare you get a higher score than mine : ( \(score) ) GET it NOW on IOS : https://itunes.apple.com/us/app/oscar-cookie/id1099453391?mt=8")
fbShare.addImage(UIImage(named: "start.png"))
self.presentViewController(fbShare, animated: true, completion: nil)
}
else
{
var alert = UIAlertController(title: "Account", message: "Please login to Facebook to share", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func startButtonFun(sender: AnyObject) {
startButton.hidden = true
startView.hidden = true
btn.hidden = false
player.pause()
}
#IBAction func playAgain(sender: AnyObject) {
player.pause()
//gameOverLbl.hidden = true
scoreLabel.hidden = true
again.hidden = true
btn.hidden = false
highScore.hidden = true
scr.hidden = false
game_over.hidden = true
fbBtn.hidden = true
twBtn.hidden = true
gameActive = true
watch = true
score = 0
scr.text = "0"
gameTimer.text = "8"
if watch1 == false
{
timer_anim.invalidate()
watch1 = true
}
}
#IBAction func button(sender: AnyObject) {
if score % 5 == 0
{
let audioPath = NSBundle.mainBundle().pathForResource("chew2", ofType: "mp3")!
do
{
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath : audioPath))
player.play()
}
catch
{
}
}
else
{
let audioPath = NSBundle.mainBundle().pathForResource("chew1", ofType: "mp3")!
do
{
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath : audioPath))
player.play()
}
catch
{
}
}
keepPlaying()
}
// func to locate the bottong randomly to all sizes
func randomLocating () {
var randomH = Int()
var randomW = Int()
if UIDevice().userInterfaceIdiom == .Phone {
switch UIScreen.mainScreen().nativeBounds.height {
case 480:
//print("iPhone Classic")
randomH = Int(arc4random_uniform(380))
randomW = Int(arc4random_uniform(260))
while randomH < 50 || randomW < 40
{
randomH = Int(arc4random_uniform(380))
randomW = Int(arc4random_uniform(260))
}
case 960:
//print("iPhone 4 or 4S")
randomH = Int(arc4random_uniform(380))
randomW = Int(arc4random_uniform(270))
while randomH < 50 || randomW < 40
{
randomH = Int(arc4random_uniform(380))
randomW = Int(arc4random_uniform(270))
}
case 1136:
// print("iPhone 5 or 5S or 5C")
randomH = Int(arc4random_uniform(520))
randomW = Int(arc4random_uniform(300))
while randomH < 40 || randomW < 40
{
randomH = Int(arc4random_uniform(520))
randomW = Int(arc4random_uniform(300))
}
case 1334:
// print("iPhone 6 or 6S")
randomH = Int(arc4random_uniform(550))
randomW = Int(arc4random_uniform(300))
while randomH < 35 || randomW < 40
{
randomH = Int(arc4random_uniform(550))
randomW = Int(arc4random_uniform(300))
}
case 2208:
// print("iPhone 6+ or 6S+")
randomH = Int(arc4random_uniform(700))
randomW = Int(arc4random_uniform(350))
while randomH < 40 || randomW < 40
{
randomH = Int(arc4random_uniform(700))
randomW = Int(arc4random_uniform(350))
}
default:
print("unknown")
}
}
else if UIDevice().userInterfaceIdiom == .Pad {
switch UIScreen.mainScreen().nativeBounds.height {
case 1024:
//print("iPad Classic")
randomH = Int(arc4random_uniform(950))
randomW = Int(arc4random_uniform(700))
while randomH < 50 || randomW < 40
{
randomH = Int(arc4random_uniform(950))
randomW = Int(arc4random_uniform(700))
}
case 2048:
//print("iPad Retina")
randomH = Int(arc4random_uniform(700))
randomW = Int(arc4random_uniform(350))
while randomH < 100 || randomW < 100
{
randomH = Int(arc4random_uniform(700))
randomW = Int(arc4random_uniform(350))
}
default:
print("unknown")
}
}
btn.frame.origin = CGPoint(x: randomW, y: randomH)
}
// func to end the game when someone loses
func endGame() {
btn.hidden = true
scoreLabel.text = "\(score)"
if hiScore < score
{
hiScore = score
highScore.text = "\(hiScore)"
}
else
{
highScore.text = "\(hiScore)"
}
NSUserDefaults.standardUserDefaults().setObject(hiScore, forKey: "high")
scoreLabel.hidden = false
again.hidden = false
highScore.hidden = false
scr.hidden = true
game_over.hidden = false
fbBtn.hidden = false
twBtn.hidden = false
let audioPath = NSBundle.mainBundle().pathForResource("end", ofType: "mp3")!
do
{
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath : audioPath))
player.play()
}
catch
{
}
}
func animating()
{
if counter < 5
{
counter += 1
}
startView.image = UIImage(named: "start-\(counter).png")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//gameOverLbl.hidden = true
scoreLabel.hidden = true
again.hidden = true
highScore.hidden = true
btn.hidden = true
game_over.hidden = true
game_over.hidden = true
fbBtn.hidden = true
twBtn.hidden = true
if watch1 == true
{
timer_anim = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("animating"), userInfo: nil, repeats: true)
watch1 = false
}
if NSUserDefaults.standardUserDefaults().objectForKey("high") != nil
{
var returnHigh = NSUserDefaults.standardUserDefaults().objectForKey("high") as! Int
hiScore = returnHigh
}
else
{
}
let audioPath = NSBundle.mainBundle().pathForResource("start", ofType: "mp3")!
do
{
try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath : audioPath))
player.play()
}
catch
{
}
self.bannerView.adUnitID = "ca-app-pub-8964973200415729/4584433890"
self.bannerView.rootViewController = self
var request:GADRequest = GADRequest()
self.bannerView.loadRequest(request)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/// to make the screen not rotate
override func shouldAutorotate() -> Bool {
return false
}
func keepPlaying()
{
score += 1
scr.text = "\(score)"
randomLocating()
if score < 5
{
seconds = 8
}
else
{
seconds = 5
}
if watch == true
{
timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("click1"), userInfo: nil, repeats: true)
}
watch = false
gameTimer.text = "\(seconds)"
}
func click1 ()
{
gameTimer.text = "\(seconds)"
if seconds == 0 && gameActive == false
{
timer.invalidate()
endGame()
btn.hidden = true
turn += 1
if turn == 1
{
admobInterstitial = createAndLoadInterstitial()
timerAds = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: Selector("presentInterstitial"), userInfo: nil, repeats: false)
turn = 0
}
}
else if seconds == 1
{
btn.hidden = true
gameActive = false
}
else
{
btn.hidden = false
}
seconds -= 1
}
func createAndLoadInterstitial()->GADInterstitial {
var interstitial = GADInterstitial(adUnitID: "ca-app-pub-8964973200415729/8720255492")
///interstitial.delegate = self
interstitial.loadRequest(GADRequest())
return interstitial
}
func presentInterstitial() {
if let isReady = admobInterstitial?.isReady {
admobInterstitial?.presentFromRootViewController(self)
}
}
func interstitial(ad: GADInterstitial!, didFailToReceiveAdWithError error: GADRequestError!) {
print("interstitialDidFailToReceiveAdWithError:\(error.localizedDescription)")
admobInterstitial = createAndLoadInterstitial()
}
}
Simply change your turn counter. Instead of if turn == 1 change it to something else. Want an ad every 3 gameovers? Change it to 3. For example:
turn = turn + 1
if turn == 3 {
admobInterstitial = createAndLoadInterstitial()
timerAds = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(ViewController.presentInterstitial), userInfo: nil, repeats: false)
turn = 0
}
I just update my code, but the alertWindow won't show up.
I add break point in Xcode, it shows that the viewDelegate is nil.
All delegates are nil.
This is my code:
class LoginViewController:UIViewController,AlertDelegate
{
override func viewDidLoad() {
super.viewDidLoad()
let BackGroundImage:UIImageView = UIImageView(frame: CGRectMake(0, 0, self.view.frame.width , self.view.frame.height))
let image: UIImage = UIImage(named: "backgroundLogin.jpg")!
BackGroundImage.image = image
self.view.addSubview(BackGroundImage)
//self.view.backgroundColor = UIColor(patternImage: UIImage(named: "backgroundLogin.jpg")!)
username.text = "User name"
password.text = "Password"
usernameWarning.text = ""
passwordWarning.text = ""
self.view.bringSubviewToFront(username)
self.view.bringSubviewToFront(password)
self.view.bringSubviewToFront(usernameText)
self.view.bringSubviewToFront(passwordText)
self.view.bringSubviewToFront(LoginButton)
self.view.bringSubviewToFront(RegisterButton)
self.view.bringSubviewToFront(usernameWarning)
self.view.bringSubviewToFront(passwordWarning)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier
{
case .Some("RegisterSegue"):
let registerViewController = segue.destinationViewController as! RegisterViewController
if let temp_register_username = usernameText!.text
{
registerViewController.register_name = temp_register_username
if let temp_register_pass = passwordText!.text
{
registerViewController.register_password = temp_register_pass
}
}
case .Some("loginSegue"):
let loginViewController = segue.destinationViewController as! FunctionsViewController
loginViewController.client = client
default:
super.prepareForSegue(segue, sender: sender)
}
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if identifier == "loginSegue"
{
if usernameText.text != ""
{
if passwordText.text != ""
{
let encrypt = encryptMsg(send: passwordText.text!)
let encrypt_pass = encrypt.encryptSendMsg()
let sendMsg = AppConfig.AddHead("login", body: usernameText.text!+"|"+encrypt_pass)
let (_,_) = client.connect(timeout: 1)
let (success,_) = client.send(str: sendMsg)
if success
{
let data = client.read(1024*10)
if let d = data
{
if let str = String(bytes: d, encoding: NSUTF8StringEncoding)
{
if str[0...2] == AppConfig.TransmissionAgreementConfiguration["successLogin"]
{
AppConfig.StoreUserID(str)
let defaults = NSUserDefaults()
defaults.setObject(usernameText.text!, forKey: "userName")
let acceptThread = AcceptThread()
acceptThread.start()
return true
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginUsernameNotExist"]
{
usernameWarning.text = "User name doesn't exist"
usernameWarning.reloadInputViews()
passwordWarning.text = ""
passwordWarning.reloadInputViews()
return false
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginUsernameAlreadyOnline"]
{
usernameWarning.text = "User account already be online"
usernameWarning.reloadInputViews()
passwordWarning.text = ""
passwordWarning.reloadInputViews()
return false
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginPassword"]
{
usernameWarning.text = ""
usernameWarning.reloadInputViews()
passwordWarning.text = "Password Incorrect"
passwordWarning.reloadInputViews()
return false
}
}
}
}
return false
}
else
{
usernameWarning.text = ""
usernameWarning.reloadInputViews()
passwordWarning.text = "password cannot be empty"
passwordWarning.reloadInputViews()
return false
}
}
else
{
usernameWarning.text = "username cannot be empty"
usernameWarning.reloadInputViews()
return false
}
}
else
{
return true
}
}
func presentWindow(title:String,msg:String)
{
let alertController = UIAlertController(title: "\(title):", message: "\(msg)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addTextFieldWithConfigurationHandler{
(textField: UITextField!) -> Void in
textField.placeholder = "Message"
}
let cancelAction = UIAlertAction(title: "Ignore", style: UIAlertActionStyle.Cancel, handler: nil)
let okAction = UIAlertAction(title: "Reply", style: UIAlertActionStyle.Default,
handler: {
action in
let reply_msg = alertController.textFields!.first! as UITextField
print("I replies \(reply_msg)")
})
alertController.addAction(cancelAction)
alertController.addAction(okAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
#IBAction func logoutUnwindSegue(segue: UIStoryboardSegue) {
// Intentionally left blank
}
var client:TCPClient = TCPClient(addr:"localhost",port:8889)
var AppConfig=TransmissionAgreement(first:"test")
#IBOutlet weak var usernameWarning: UILabel!
#IBOutlet weak var passwordWarning: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var password: UILabel!
#IBOutlet weak var usernameText: UITextField!
#IBOutlet weak var passwordText: UITextField!
#IBOutlet weak var LoginButton: UIButton!
#IBOutlet weak var RegisterButton: UIButton!
}
class AcceptThread: NSThread {
override func main() {
let server:TCPServer = TCPServer(addr:"127.0.0.1",port: 7000)
let (success,_) = server.listen()
if success
{
while !cancelled
{
if let my_client = server.accept()
{
let clientReadThread = ClientReadThread(client: my_client)
clientReadThread.start()
clientThreads.append(clientReadThread)
}
}
for clientThread in clientThreads {
clientThread.cancel()
}
}
}
var clientThreads = Array<ClientReadThread>()
var AppConfig=TransmissionAgreement(first:"test")
}
#objc protocol AlertDelegate: class {
func presentWindow(title:String,msg:String)
}
#objc protocol ClientReadThreadDelegate: class {
func clientReadThread(clientReadThread: ClientReadThread, didReceiveMessage message: String)
}
class ClientReadThread:NSThread{
init(client: TCPClient) {
self.client = client
super.init()
}
override func main() {
while !cancelled, let readValue = client.read(1024*10) {
if let message = String(bytes: readValue, encoding: NSUTF8StringEncoding) {
let head = message[0...2]
// print("head is \(head)")
if head! == AppConfig.TransmissionAgreementConfiguration["add"]
{
let defaults = NSUserDefaults()
let myname = defaults.stringForKey("userName")
//store data
let context = CoreDataService.sharedCoreDataService.mainQueueContext
let friend = NSEntityDescription.insertNewObjectForNamedEntity(Friends.self, inManagedObjectContext: context)
friend.name = message.substringFromIndex(3)
try! context.save()
let sendMsg = AppConfig.AddClientHead("agreeAdd", body:myname)
let (_,_) = client.send(str:sendMsg)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate?.clientReadThread(self, didReceiveMessage: message)
})
}
else if head! == AppConfig.TransmissionAgreementConfiguration["chat"]
{
let search = message.substringFromIndex(3)
let resultController = try? catFriend.sharedCatFriendsService.catBlack(search)
try! resultController!.performFetch()
let flag = resultController?.sections?.count
if flag <= 1//friend
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let client_info = message.split("|")
let client_name = client_info[1]
//self.alertDelegate?.presentWindow(client_name, msg: client_info[2])
self.viewDelegate?.presentWindow(client_name, msg: client_info[2])
})
}
else//black, not show the meg
{
//do nothing
}
}
}
}
}
var AppConfig=TransmissionAgreement(first:"test")
let client: TCPClient
var viewDelegate:LoginViewController?
var delegate: ClientReadThreadDelegate?
var alertDelegate: AlertDelegate?
private var resultController: NSFetchedResultsController?
}
presentViewContoller is a method on UIViewController so you'll need an instance of a view controller to be able to present your alert view.
The way this would normally be done is to have the view controller you have start the ClientReadThread implement the AlertDelegate protocol. There's no reason for ClientReadThread to implement AlertDelegate itself. Would it ever be it's own delegate?
Also you delegate's should be marked weak or else you'll end up with a retain cycle.
class MyViewController : UIViewController, AlertDelegate {
private var clientReadThread: ClientReadThread?
override viewDidLoad() {
super.viewDidLoad();
clientReadThread = ClientReadThread(someTcpClient)
clientReadThread.viewDelegate = self
}
func presentWindow(title:String,msg:String) {
// Your code goes here.
}
}
Your other option would be to pass an instance of a UIViewController into your ClientReadThread and use that to present the alert view. Although that's not the way I would go. the ClientReadThread shouldn't need to know how it's alerts are handled, that should be delegated to another class.