Memory won't be released until APP enters background - ios

I'm working on a app with a side menu, using two ViewController in a ContainController.
And when after close the side menu ViewController(the deinit method is called), the memory just would not be released(it's about 23 MiB), until the app entered background.
The "Leaks" tool build in Xcode 9 says there's no memory leak at all...
Anyone could tell me what's wrong?
Thanks a lot in advance!
import UIKit
import CoreData
enum MenuState {
case Collapsed
case Expanding
case Expanded
}
class ContainerViewController: UIViewController {
weak var managedObjectContext: NSManagedObjectContext!
weak var mainViewController: MasterViewController!
weak var menuViewController: MenuViewController?
var currentState = MenuState.Collapsed {
didSet {
let shouldShowShadow = currentState != .Collapsed
showShadowForMainViewController(shouldShowShadow: shouldShowShadow)
}
}
override func viewDidLoad() {
super.viewDidLoad()
mainViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "mainView") as! MasterViewController
view.addSubview(mainViewController.view)
DispatchQueue.global(qos: .userInteractive).async {
self.mainViewController.managedObjectContext = self.managedObjectContext
self.addChildViewController(self.mainViewController)
self.mainViewController.didMove(toParentViewController: self)
}
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action:#selector(self.handlePanGesture(_:)))
self.mainViewController.view.addGestureRecognizer(panGestureRecognizer)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(self.handleTapGesture))
self.mainViewController.view.addGestureRecognizer(tapGestureRecognizer)
}
#objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
switch(recognizer.state) {
case .began:
let dragFromLeftToRight = (recognizer.velocity(in: view).x > 0)
if (currentState == .Collapsed && dragFromLeftToRight) {
currentState = .Expanding
addMenuViewController()
}
case .changed:
let positionX = recognizer.view!.frame.origin.x + recognizer.translation(in: view).x
recognizer.view!.frame.origin.x = positionX < 0 ? 0 : positionX
let boundX = mainViewController.view.frame.width - menuViewExpandedOffset
if positionX > 0 {
recognizer.view!.frame.origin.x = positionX > boundX ? boundX : positionX
}
recognizer.setTranslation(.zero, in: view)
case .ended:
let boundX = mainViewController.view.frame.width - menuViewExpandedOffset
let hasMovedhanHalfway = (recognizer.view!.frame.origin.x > boundX * 0.3)
animateMainView(shouldExpand: hasMovedhanHalfway)
default:
break
}
}
#objc func handleTapGesture() {
if currentState == .Expanded {
animateMainView(shouldExpand: false)
}
}
//ADD MENUVC
func addMenuViewController() {
if (menuViewController == nil) {
menuViewController = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "menuView")
as? MenuViewController
view.insertSubview(menuViewController!.view, at: 0)
addChildViewController(menuViewController!)
menuViewController!.didMove(toParentViewController: self)
}
}
let menuViewExpandedOffset: CGFloat = 480
func animateMainView(shouldExpand: Bool) {
if (shouldExpand) {
currentState = .Expanded
animateMainViewXPosition(targetPosition: mainViewController.view.frame.width -
menuViewExpandedOffset)
}
else {
animateMainViewXPosition(targetPosition: 0) { finished in
self.currentState = .Collapsed
self.menuViewController?.view.removeFromSuperview()
self.menuViewController?.removeFromParentViewController()
self.menuViewController = nil
}
}
}
func animateMainViewXPosition(targetPosition: CGFloat,
completion: ((Bool) -> Void)! = nil) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1.0,
initialSpringVelocity: 0, options: .curveEaseOut, animations: {
self.mainViewController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func showShadowForMainViewController(shouldShowShadow: Bool) {
if (shouldShowShadow) {
mainViewController.cameraBGimage.layer.shadowOpacity = 0.6
mainViewController.cameraBGimage.layer.shadowOffset = CGSize(width: 0, height: 1)
} else {
mainViewController.cameraBGimage.layer.shadowOpacity = 0.0
}
}
}

Provably there is nothing wrong with your app. The iOS system will manage the memory the way it thinks it is best.
If your objects are deallocated when they should be, then you are fine. If you want to get a better understanding of when your views are deallocated you can always use de Allocations tool of the Instruments and filter it by your app's name.

Related

How to show corrected and incorrected count of card swiped either right or left from one view to another

In my app, i have implemented card swipe feature using ZLSwipeable library. Now when i swipe right, means the answer is correct and swipe left then answer is incorrect. Lets suppose, i am having 5 cards then if i swipe 2 left and 3 right, then the corrected count should be 2 and incorrect count should be 3 which should be displayed on another screen named as Score VC which has 2 labels for corrected and incorrected count. When i swipe the last card, then it should navigate to Score VC, how do i implement it. the card first is flipped and then swiped or clicked. Means the flipped card also have two buttons on it , either you swipe left or click tick button, it does the same functionality and same goes for right swipe
My Flipcard VC code where the card swipe feature reside:
class FlipCardVC: UIViewController, UIGestureRecognizerDelegate, AVSpeechSynthesizerDelegate
{
#IBOutlet weak var exitTapPopup: UIView!
#IBOutlet weak var cardFlipPopup: UIView!
#IBOutlet weak var savePopup: UIView!
#IBOutlet var hideCardPopup: UIView!
#IBOutlet var audioAllowPopup: UIView!
#IBOutlet weak var switchState: UISwitch!
#IBOutlet weak var progressViewBar: UIProgressView!
#IBOutlet weak var cardOuterView: UIView!
var wordsData = [ModelResult]()
var cardId = Int()
var cardSoundIndex = 0
//MARK: ZLSwipeableView Properties
#IBOutlet weak var SwipeCardOuterView: UIView!
var swipeableView: ZLSwipeableView!
var colorIndex = 0
var cardIndex = 0
var loadCardsFromXib = false
let speechSynthesizer = AVSpeechSynthesizer()
var isCorrect: Bool = true
var correctAnswerArr = [String]()
var incorrectAnswerArr = [String]()
var cardView:CardView?
var trueCounter = 0
var falseCounter = 0
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
constrain(SwipeCardOuterView, view) { view1, view2 in
view1.left == view2.left
view1.top == view2.top
view1.width == view.bounds.width
view1.height == view.bounds.height
}
}
// MARK: CARD VIEW
func cardInit()
{
wordsData.shuffle()
swipeableView = ZLSwipeableView()
view.addSubview(swipeableView)
swipeableView.numberOfActiveView = 2
swipeableView.didStart = {view, location in
print("Did start swiping view at location: \(location)")
}
swipeableView.swiping = {view, location, translation in
print("Swiping at view location: \(location) translation: \(translation)")
}
swipeableView.didEnd = {view, location in
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
if self.swipeableView.direction == .Left
{
self.isCorrect = true
self.correctAnswerArr.append("\(self.isCorrect)")
print(self.correctAnswerArr.count)
if self.colorIndex == self.wordsData.count
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
vc.tempCorrectAnswer = "\(self.correctAnswerArr.count)"
self.navigationController?.pushViewController(vc, animated: true)
}
}
else
{
self.isCorrect = false
self.incorrectAnswerArr.append("\(self.isCorrect)")
print(self.incorrectAnswerArr.count)
if self.colorIndex == self.wordsData.count
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
vc.tempincorrectAnswer = "\(self.incorrectAnswerArr.count)"
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
swipeableView.didSwipe = {view, direction, vector in
print("Did swipe view in direction: \(direction), vector: \(vector)")
if direction.description == "Right"
{
// self.UserObject = self.UserData[view.tag]
// self.API_POST_Favorite()
}
else
{
// self.UserObject = self.UserData[view.tag]
// self.API_POST_UNFavorite()
}
}
swipeableView.didCancel = {view in
print("Did cancel swiping view")
}
swipeableView.didTap = {view, location in
let cv = view as! CardView
if cv.contentview.tag == 0 {
cv.contentview.tag = 1
UIView.transition(with: view, duration: 0.7, options: .transitionFlipFromRight, animations: {
if self.wordsData.count > cv.tag
{
let data = self.wordsData[cv.tag] as ModelResult
let tempLangChanged = UserDefaults.standard.bool(forKey: "isLangChanged")
if tempLangChanged != false
{
if let englishName = data.cardWordEnglish, englishName.count != 0
{
cv.contentview.wordLabel.text = englishName
}
cv.contentview.wordImage.isHidden = true
cv.contentview.yesNoBtnView.isHidden = false
cv.contentview.wordLabel.isUserInteractionEnabled = true
let tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.labelAction(gr:)))
cv.contentview.wordLabel.addGestureRecognizer(tap)
tap.delegate = self
cv.contentview.yesBtn.tag = cv.tag
cv.contentview.noBtn.tag = cv.tag
cv.contentview.yesBtn.addTarget(self, action: #selector(self.yesBtnSwipeLeft), for: .touchUpInside)
cv.contentview.noBtn.addTarget(self, action: #selector(self.noBtnSwipeRight), for: .touchUpInside)
let speechUtterance: AVSpeechUtterance = AVSpeechUtterance(string: data.cardWordEnglish!)
speechUtterance.rate = 0.3
speechUtterance.volume = 1.0
speechUtterance.voice = AVSpeechSynthesisVoice(language: "en-US")
self.speechSynthesizer.speak(speechUtterance)
}
else
{
if let spanishName = data.cardWordSpanish, spanishName.count != 0
{
cv.contentview.wordLabel.text = spanishName
}
cv.contentview.wordImage.isHidden = true
cv.contentview.yesNoBtnView.isHidden = false
cv.contentview.wordLabel.isUserInteractionEnabled = true
let tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.labelAction(gr:)))
cv.contentview.wordLabel.addGestureRecognizer(tap)
tap.delegate = self
cv.contentview.yesBtn.tag = cv.tag
cv.contentview.noBtn.tag = cv.tag
cv.contentview.yesBtn.addTarget(self, action: #selector(self.yesBtnSwipeLeft), for: .touchUpInside)
cv.contentview.noBtn.addTarget(self, action: #selector(self.noBtnSwipeRight), for: .touchUpInside)
let speechUtterance: AVSpeechUtterance = AVSpeechUtterance(string: data.cardWordSpanish!)
speechUtterance.rate = 0.3
speechUtterance.volume = 1.0
speechUtterance.voice = AVSpeechSynthesisVoice(language: "es")
self.speechSynthesizer.speak(speechUtterance)
}
}
}, completion: { (complete) in
})
}
}
swipeableView.didDisappear = { view in
print("Did disappear swiping view")
}
constrain(swipeableView, view) { view1, view2 in
view1.left == view2.left+5
view1.right == view2.right-5
view1.top == view2.top + 50
view1.bottom == view2.bottom - 16 + (IS_IPHONE_X() ? -24 : 0)
// view1.left == view2.left+50
// view1.right == view2.right-50
// view1.top == view2.top + 120
// view1.bottom == view2.bottom - 100
// view1.left == view2.left+16
// view1.right == view2.right-16
// view1.top == view2.top + 83 + (IS_IPHONE_X() ? 24 : 0)
// view1.bottom == view2.bottom - 16 + (IS_IPHONE_X() ? -24 : 0)
}
self.loadCardsFromXib = true
self.colorIndex = 0
self.swipeableView.discardViews()
self.swipeableView.loadViews()
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
if let swview = swipeableView
{
swview.nextView = {
return self.nextCardView()
}
}
}
#objc func yesBtnSwipeLeft(sender: UIButton) {
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
self.swipeableView.swipeTopView(inDirection: .Left)
trueCounter = trueCounter + 1
if colorIndex == self.wordsData.count{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
UserDefaults.standard.set(trueCounter, forKey: "trueCounter")
self.navigationController?.pushViewController(vc, animated: true)
}
}
#objc func noBtnSwipeRight(sender: UIButton) {
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
self.swipeableView.swipeTopView(inDirection: .Right)
falseCounter = falseCounter + 1
print("THIS IS my counter: (falseCounter)")
if colorIndex == self.wordsData.count {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
UserDefaults.standard.set(falseCounter, forKey: "falseCounter")
self.navigationController?.pushViewController(vc, animated: true)
}
}
#objc func soundBtn(sender: UIButton)
{
ApiUtillity.sharedInstance.AddSubViewtoParentView(parentview: self.view, subview: self.audioAllowPopup)
}
#objc func hideCard(sender: UIButton)
{
ApiUtillity.sharedInstance.AddSubViewtoParentView(parentview: self.view, subview: self.hideCardPopup)
}
func nextCardView() -> UIView?
{
let arrayData = wordsData
if (self.colorIndex >= arrayData.count)
{
return nil
}
self.cardView = CardView(frame: swipeableView.bounds)
if loadCardsFromXib
{
self.cardView!.contentview = Bundle.main.loadNibNamed("CardContentView", owner: self, options: nil)?.first! as? CardView
self.cardView!.contentview.superVC = self
self.cardView!.contentview.translatesAutoresizingMaskIntoConstraints = false
self.cardView!.contentview.backgroundColor = self.cardView!.backgroundColor
self.cardView!.addSubview(self.cardView!.contentview)
let data = arrayData[(self.colorIndex)] as ModelResult
self.cardView!.contentview.wordModel = data
self.cardView!.contentview.currentCardCountLbl.text = "\(self.colorIndex + 1)"+" "+"of"+" "+"\(wordsData.count)"
let tempLangChanged = UserDefaults.standard.bool(forKey: "isLangChanged")
if tempLangChanged == false
{
if let dict = data.cardWordImage, dict.count != 0
{
let url = WS_wordIconUrl + dict
self.cardView!.contentview.wordImage.kf.indicatorType = .activity
self.cardView!.contentview.wordImage.kf.setImage(with: URL(string: url))
}
if let englishName = data.cardWordEnglish, englishName.count != 0
{
self.cardView!.contentview.wordLabel.text = englishName
}
cardView?.contentview.yesNoBtnView.isHidden = true
}
else
{
if let dict = data.cardWordImage, dict.count != 0
{
let url = WS_wordIconUrl + dict
self.cardView!.contentview.wordImage.kf.indicatorType = .activity
self.cardView!.contentview.wordImage.kf.setImage(with: URL(string: url))
}
if let spanishName = data.cardWordSpanish, spanishName.count != 0
{
self.cardView!.contentview.wordLabel.text = spanishName
}
cardView?.contentview.yesNoBtnView.isHidden = true
}
self.cardView!.contentview.soundBtn.addTarget(self, action:#selector(self.soundBtn(sender:)), for: .touchUpInside)
self.cardView!.contentview.hideCard.addTarget(self, action:#selector(self.hideCard(sender:)), for: .touchUpInside)
self.cardView!.contentview.soundBtn.tag = self.colorIndex
self.cardView!.contentview.hideCard.tag = self.colorIndex
self.cardView!.tag = self.colorIndex
constrain(self.cardView!.contentview, self.cardView!) { view1, view2 in
view1.left == view2.left
view1.top == view2.top
view1.width == self.cardView!.bounds.width
view1.height == self.cardView!.bounds.height
}
}
colorIndex += 1
return cardView
}
//MARK: API Parsing for deck words data
func WS_GetWords()
{
if ApiUtillity.sharedInstance.isReachable()
{
ApiUtillity.sharedInstance.StartProgress(view: self.view)
let url = "\(WS_GetWordsAPI)/\(cardId)"
APIClient<ModelBaseGetWordsData>().API_GET(Url: url, Params: [:], Authentication: true, Progress: true, Alert: true, Offline: false, SuperVC: self, completionSuccess: { (modelResponse) in
if(modelResponse.success == true)
{
ApiUtillity.sharedInstance.StopProgress(view: self.view)
self.colorIndex = 0
self.wordsData.removeAll()
if let array = modelResponse.result, array.count != 0
{
for data in array
{
self.wordsData.append(data)
}
self.cardInit()
}
else
{
self.colorIndex = 0
self.wordsData.removeAll()
DispatchQueue.main.asyncAfter(deadline: .now(), execute:
{
if self.swipeableView != nil
{
self.swipeableView.removeFromSuperview()
}
self.cardInit()
})
}
}
else
{
self.showToast(message: modelResponse.message!)
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
if self.wordsData.count == 0
{
self.setNoDataMessage(message: modelResponse.message)
}
}) { (failed) in
self.showToast(message: failed.localizedDescription)
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
}
else
{
self.showToast(message: "No internet connection...")
}
}
my score vc code:
class ScoreVC: UIViewController {
#IBOutlet weak var progressScoreView: MBCircularProgressBarView!
#IBOutlet weak var correctAnswerLbl: UILabel!
#IBOutlet weak var incorrectAnswerLbl: UILabel!
var tempCorrectAnswer = String()
var tempincorrectAnswer = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.progressScoreView.value = 0.0
let correct = UserDefaults.standard.string(forKey: "trueCounter")
let incorrect = UserDefaults.standard.string(forKey: "falseCounter")
self.correctAnswerLbl.text = correct
self.incorrectAnswerLbl.text = incorrect
}
override func viewWillAppear(_ animated: Bool) {
self.setCounterToZero()
UIView.animate(withDuration: 10.0) {
self.progressScoreView.value = 50.0
}
}
func setCounterToZero(){
correctAnswerLbl.text = ""
incorrectAnswerLbl.text = ""
}
create 2 var variables to increase the value
var trueCounter = 0
var falseCounter = 0
Set a function in your ViewWillAppear for every time your screen reloads the counter do it aswell
override func viewWillAppear(_ animated: Bool) {
setCounterToZero()
}
func setCounterToZero(){
correctAnswerLbl.text = ""
incorrectAnswerLbl.text = ""
}
so in your swipe functions set the variables to increase
for false:
falseCounter = falseCounter + 1
for true
trueCounter = trueCounter + 1
when the game ends and sends the user to the ScoreVC
UserDefaults.standard.set(trueCounter, forKey: "trueCounter")
UserDefaults.standard.set(falseCounter, forKey: "falseCounter")
flipcardVC
class FlipCardVC: UIViewController, UIGestureRecognizerDelegate, AVSpeechSynthesizerDelegate
{
#IBOutlet weak var exitTapPopup: UIView!
#IBOutlet weak var cardFlipPopup: UIView!
#IBOutlet weak var savePopup: UIView!
#IBOutlet var hideCardPopup: UIView!
#IBOutlet var audioAllowPopup: UIView!
#IBOutlet weak var switchState: UISwitch!
#IBOutlet weak var progressViewBar: UIProgressView!
#IBOutlet weak var cardOuterView: UIView!
var wordsData = [ModelResult]()
var cardId = Int()
var cardSoundIndex = 0
//MARK: ZLSwipeableView Properties
#IBOutlet weak var SwipeCardOuterView: UIView!
var swipeableView: ZLSwipeableView!
var colorIndex = 0
var cardIndex = 0
var loadCardsFromXib = false
let speechSynthesizer = AVSpeechSynthesizer()
var isCorrect: Bool = true
var correctAnswerArr = [String]()
var incorrectAnswerArr = [String]()
var trueCounter = 0
var falseCounter = 0
var cardView:CardView?
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
constrain(SwipeCardOuterView, view) { view1, view2 in
view1.left == view2.left
view1.top == view2.top
view1.width == view.bounds.width
view1.height == view.bounds.height
}
}
// MARK: CARD VIEW
func cardInit()
{
wordsData.shuffle()
swipeableView = ZLSwipeableView()
view.addSubview(swipeableView)
swipeableView.numberOfActiveView = 2
swipeableView.didStart = {view, location in
print("Did start swiping view at location: \(location)")
}
swipeableView.swiping = {view, location, translation in
print("Swiping at view location: \(location) translation: \(translation)")
}
swipeableView.didEnd = {view, location in
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
if self.swipeableView.direction == .Left
{
self.isCorrect = true
self.correctAnswerArr.append("\(self.isCorrect)")
print(self.correctAnswerArr.count)
if self.colorIndex == self.wordsData.count
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
vc.tempCorrectAnswer = "\(self.correctAnswerArr.count)"
self.navigationController?.pushViewController(vc, animated: true)
}
}
else
{
self.isCorrect = false
self.incorrectAnswerArr.append("\(self.isCorrect)")
print(self.incorrectAnswerArr.count)
if self.colorIndex == self.wordsData.count
{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
vc.tempincorrectAnswer = "\(self.incorrectAnswerArr.count)"
self.navigationController?.pushViewController(vc, animated: true)
}
}
}
swipeableView.didSwipe = {view, direction, vector in
print("Did swipe view in direction: \(direction), vector: \(vector)")
if direction.description == "Right"
{
// self.UserObject = self.UserData[view.tag]
// self.API_POST_Favorite()
}
else
{
// self.UserObject = self.UserData[view.tag]
// self.API_POST_UNFavorite()
}
}
swipeableView.didCancel = {view in
print("Did cancel swiping view")
}
swipeableView.didTap = {view, location in
let cv = view as! CardView
if cv.contentview.tag == 0 {
cv.contentview.tag = 1
UIView.transition(with: view, duration: 0.7, options: .transitionFlipFromRight, animations: {
if self.wordsData.count > cv.tag
{
let data = self.wordsData[cv.tag] as ModelResult
let tempLangChanged = UserDefaults.standard.bool(forKey: "isLangChanged")
if tempLangChanged != false
{
if let englishName = data.cardWordEnglish, englishName.count != 0
{
cv.contentview.wordLabel.text = englishName
}
cv.contentview.wordImage.isHidden = true
cv.contentview.yesNoBtnView.isHidden = false
cv.contentview.wordLabel.isUserInteractionEnabled = true
let tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.labelAction(gr:)))
cv.contentview.wordLabel.addGestureRecognizer(tap)
tap.delegate = self
cv.contentview.yesBtn.tag = cv.tag
cv.contentview.noBtn.tag = cv.tag
cv.contentview.yesBtn.addTarget(self, action: #selector(self.yesBtnSwipeLeft), for: .touchUpInside)
cv.contentview.noBtn.addTarget(self, action: #selector(self.noBtnSwipeRight), for: .touchUpInside)
let speechUtterance: AVSpeechUtterance = AVSpeechUtterance(string: data.cardWordEnglish!)
speechUtterance.rate = 0.3
speechUtterance.volume = 1.0
speechUtterance.voice = AVSpeechSynthesisVoice(language: "en-US")
self.speechSynthesizer.speak(speechUtterance)
}
else
{
if let spanishName = data.cardWordSpanish, spanishName.count != 0
{
cv.contentview.wordLabel.text = spanishName
}
cv.contentview.wordImage.isHidden = true
cv.contentview.yesNoBtnView.isHidden = false
cv.contentview.wordLabel.isUserInteractionEnabled = true
let tap:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.labelAction(gr:)))
cv.contentview.wordLabel.addGestureRecognizer(tap)
tap.delegate = self
cv.contentview.yesBtn.tag = cv.tag
cv.contentview.noBtn.tag = cv.tag
cv.contentview.yesBtn.addTarget(self, action: #selector(self.yesBtnSwipeLeft), for: .touchUpInside)
cv.contentview.noBtn.addTarget(self, action: #selector(self.noBtnSwipeRight), for: .touchUpInside)
let speechUtterance: AVSpeechUtterance = AVSpeechUtterance(string: data.cardWordSpanish!)
speechUtterance.rate = 0.3
speechUtterance.volume = 1.0
speechUtterance.voice = AVSpeechSynthesisVoice(language: "es")
self.speechSynthesizer.speak(speechUtterance)
}
}
}, completion: { (complete) in
})
}
}
swipeableView.didDisappear = { view in
print("Did disappear swiping view")
}
constrain(swipeableView, view) { view1, view2 in
view1.left == view2.left+5
view1.right == view2.right-5
view1.top == view2.top + 50
view1.bottom == view2.bottom - 16 + (IS_IPHONE_X() ? -24 : 0)
// view1.left == view2.left+50
// view1.right == view2.right-50
// view1.top == view2.top + 120
// view1.bottom == view2.bottom - 100
// view1.left == view2.left+16
// view1.right == view2.right-16
// view1.top == view2.top + 83 + (IS_IPHONE_X() ? 24 : 0)
// view1.bottom == view2.bottom - 16 + (IS_IPHONE_X() ? -24 : 0)
}
self.loadCardsFromXib = true
self.colorIndex = 0
self.swipeableView.discardViews()
self.swipeableView.loadViews()
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
if let swview = swipeableView
{
swview.nextView = {
return self.nextCardView()
}
}
}
#objc func yesBtnSwipeLeft(sender: UIButton) {
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
self.swipeableView.swipeTopView(inDirection: .Left)
var finalTrue = 0
trueCounter = trueCounter + 1
print("THE USER SWIPE TO left")
print("///////////////////////////////////////////")
print(trueCounter)
print("///////////////////////////////////////////")
finalTrue = trueCounter
if colorIndex == self.wordsData.count{
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
UserDefaults.standard.set(finalTrue, forKey: "trueCounter")
self.navigationController?.pushViewController(vc, animated: true)
}
}
#objc func noBtnSwipeRight(sender: UIButton) {
self.progressViewBar.progress = (Float(self.cardIndex + 1) / Float(self.wordsData.count))
self.cardIndex += 1
self.swipeableView.swipeTopView(inDirection: .Right)
var finalFlase = 0
falseCounter = falseCounter + 1
print("THE USER SWIPE TO right")
print("///////////////////////////////////////////")
print(falseCounter)
print("///////////////////////////////////////////")
var finalFlase = falseCounter
print("THIS IS my counter: (falseCounter)")
if colorIndex == self.wordsData.count {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ScoreVC") as! ScoreVC
UserDefaults.standard.set(finalFlase, forKey: "falseCounter")
self.navigationController?.pushViewController(vc, animated: true)
}
}
#objc func soundBtn(sender: UIButton)
{
ApiUtillity.sharedInstance.AddSubViewtoParentView(parentview: self.view, subview: self.audioAllowPopup)
}
#objc func hideCard(sender: UIButton)
{
ApiUtillity.sharedInstance.AddSubViewtoParentView(parentview: self.view, subview: self.hideCardPopup)
}
func nextCardView() -> UIView?
{
let arrayData = wordsData
if (self.colorIndex >= arrayData.count)
{
return nil
}
self.cardView = CardView(frame: swipeableView.bounds)
if loadCardsFromXib
{
self.cardView!.contentview = Bundle.main.loadNibNamed("CardContentView", owner: self, options: nil)?.first! as? CardView
self.cardView!.contentview.superVC = self
self.cardView!.contentview.translatesAutoresizingMaskIntoConstraints = false
self.cardView!.contentview.backgroundColor = self.cardView!.backgroundColor
self.cardView!.addSubview(self.cardView!.contentview)
let data = arrayData[(self.colorIndex)] as ModelResult
self.cardView!.contentview.wordModel = data
self.cardView!.contentview.currentCardCountLbl.text = "\(self.colorIndex + 1)"+" "+"of"+" "+"\(wordsData.count)"
let tempLangChanged = UserDefaults.standard.bool(forKey: "isLangChanged")
if tempLangChanged == false
{
if let dict = data.cardWordImage, dict.count != 0
{
let url = WS_wordIconUrl + dict
self.cardView!.contentview.wordImage.kf.indicatorType = .activity
self.cardView!.contentview.wordImage.kf.setImage(with: URL(string: url))
}
if let englishName = data.cardWordEnglish, englishName.count != 0
{
self.cardView!.contentview.wordLabel.text = englishName
}
cardView?.contentview.yesNoBtnView.isHidden = true
}
else
{
if let dict = data.cardWordImage, dict.count != 0
{
let url = WS_wordIconUrl + dict
self.cardView!.contentview.wordImage.kf.indicatorType = .activity
self.cardView!.contentview.wordImage.kf.setImage(with: URL(string: url))
}
if let spanishName = data.cardWordSpanish, spanishName.count != 0
{
self.cardView!.contentview.wordLabel.text = spanishName
}
cardView?.contentview.yesNoBtnView.isHidden = true
}
self.cardView!.contentview.soundBtn.addTarget(self, action:#selector(self.soundBtn(sender:)), for: .touchUpInside)
self.cardView!.contentview.hideCard.addTarget(self, action:#selector(self.hideCard(sender:)), for: .touchUpInside)
self.cardView!.contentview.soundBtn.tag = self.colorIndex
self.cardView!.contentview.hideCard.tag = self.colorIndex
self.cardView!.tag = self.colorIndex
constrain(self.cardView!.contentview, self.cardView!) { view1, view2 in
view1.left == view2.left
view1.top == view2.top
view1.width == self.cardView!.bounds.width
view1.height == self.cardView!.bounds.height
}
}
colorIndex += 1
return cardView
}
//MARK: API Parsing for deck words data
func WS_GetWords()
{
if ApiUtillity.sharedInstance.isReachable()
{
ApiUtillity.sharedInstance.StartProgress(view: self.view)
let url = "\(WS_GetWordsAPI)/\(cardId)"
APIClient<ModelBaseGetWordsData>().API_GET(Url: url, Params: [:], Authentication: true, Progress: true, Alert: true, Offline: false, SuperVC: self, completionSuccess: { (modelResponse) in
if(modelResponse.success == true)
{
ApiUtillity.sharedInstance.StopProgress(view: self.view)
self.colorIndex = 0
self.wordsData.removeAll()
if let array = modelResponse.result, array.count != 0
{
for data in array
{
self.wordsData.append(data)
}
self.cardInit()
}
else
{
self.colorIndex = 0
self.wordsData.removeAll()
DispatchQueue.main.asyncAfter(deadline: .now(), execute:
{
if self.swipeableView != nil
{
self.swipeableView.removeFromSuperview()
}
self.cardInit()
})
}
}
else
{
self.showToast(message: modelResponse.message!)
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
if self.wordsData.count == 0
{
self.setNoDataMessage(message: modelResponse.message)
}
}) { (failed) in
self.showToast(message: failed.localizedDescription)
ApiUtillity.sharedInstance.StopProgress(view: self.view)
}
}
else
{
self.showToast(message: "No internet connection...")
}
}
and in your score labels score vc
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.progressScoreView.value = 0.0
let correct = UserDefaults.standard.string(forKey: "trueCounter")
let incorrect = UserDefaults.standard.string(forKey: "falseCounter")
self.correctAnswerLbl.text = correct
self.incorrectAnswerLbl.text = incorrect
}
override func viewWillAppear(_ animated: Bool) {
//don't add setCounterToZero()
UIView.animate(withDuration: 10.0) {
self.progressScoreView.value = 50.0
}
}
plz rate if was useful

How to get the right position of an View for animations?

I want to create a set game App. For that, I have a deck of cards from which I want the cards to (animating) move on the board.
My problem is when I do this:
private func createDeck() {
allCards.forEach{ card in
card.isFaceUp = false
card.frame = Deck.frame
GameBoard.allCardsInDeck.append(card)
}
}
the cards appearing from another position then the deck.
Doesn´t Deck.frame give me the current position of the view in its superview? my card appearing from the top but my deckView is at the bottom.
this is my code ViewController:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var informationLabel: UILabel!
#IBOutlet weak var ScoreLabel: UILabel!
#IBOutlet weak var Deck: Deck!
#IBOutlet weak var GameBoard: CardBoardView! {
didSet {
let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipe))
swipeGesture.direction = .down
GameBoard.addGestureRecognizer(swipeGesture)
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotation))
GameBoard.addGestureRecognizer(rotationGesture)
}
}
private var gameIsStarted = false
private var game: Game = Game() {
didSet {
updateScoreLabel()
}
}
private var selectedCards = [CardSubview] ()
private var indexOfAllCards: Int = 0
private lazy var allCards = getAllCards()
private var cardsBackgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
#objc func rotation(recognizer: UIRotationGestureRecognizer){
switch recognizer.state {
case .ended: GameBoard.mixCards()
default: break
}
}
#objc private func swipe(recognizer: UISwipeGestureRecognizer) {
switch recognizer.state {
case .ended: add3Cards()
default: break
}
}
private func getAllCards () -> [CardSubview] {
var cards = [CardSubview]()
for index in 0..<game.cards.count {
cards.append(CardSubview())
cards[index].fill = game.cards[index].strokeIdentifier
cards[index].color = game.cards[index].colorIdentifier
cards[index].form = game.cards[index].form
cards[index].occurence = game.cards[index].occurenceOfForm
cards[index].addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tab)))
cards[index].backgroundColor = cardsBackgroundColor
cards[index].indexNr = index
}
return cards
}
#objc private func tab (recognizer: UITapGestureRecognizer) {
switch recognizer.state {
case .ended:
if let card = recognizer.view as? CardSubview, card.isFaceUp == true {
card.isSelected = !card.isSelected
if card.isSelected {
selectedCards.append(card)
if selectedCards.count > 2 {
let isSet = game.isSet(arrayOfIndexes: [selectedCards[0].indexNr,selectedCards[1].indexNr, selectedCards[2].indexNr]) == true
if isSet {
game.score += 1
selectedCards.forEach { card in
GameBoard.doWhenMatched(card: card)
card.isSelected = false
selectedCards.removeAll()
}
findSet()
} else if isSet == false {
game.score -= 1
selectedCards.forEach{ card in
card.isSelected = false
}
selectedCards.removeAll()
}
}
} else if card.isSelected == false, selectedCards.contains(card) {
selectedCards.remove(at: selectedCards.index(of: card)!)
}
}
default: break
}
}
private func findSet() {
if GameBoard.allOpenCards.count > 0 {
var setInOpenCards = Bool()
var allSetsInOpenCards = Int()
for index1 in 0..<GameBoard.allOpenCards.count-2 {
for index2 in (index1+1)..<GameBoard.allOpenCards.count-1 {
for index3 in (index2+1)..<GameBoard.allOpenCards.count {
setInOpenCards = game.isSet(arrayOfIndexes: [allCards[index1].indexNr, allCards[index2].indexNr, allCards[index3].indexNr])
if (setInOpenCards == true){
allSetsInOpenCards+=1
}
}
}
}
informationLabel.text = "Sets available: \(allSetsInOpenCards)"
} else {
informationLabel.text = "Sets available: \(0)"
}
}
#IBAction private func newGame() {
game = Game()
GameBoard.removeSubviewsFromDeck()
GameBoard.allCardsInDeck.removeAll()
createDeck()
GameBoard.backgroundColor = UIColor.black
GameBoard.removeSubviews()
selectedCards.removeAll()
gameIsStarted = true
allCards = getAllCards()
indexOfAllCards = 0
GameBoard.allOpenCards.removeAll()
add12Cards()
}
private func updateScoreLabel () {
if game.score >= 0 {
ScoreLabel.backgroundColor = #colorLiteral(red: 0, green: 0.9768045545, blue: 0, alpha: 0.5)
} else if game.score < 0 {
ScoreLabel.backgroundColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 0.5)
}
ScoreLabel.text = "Score: \(game.score)"
}
private func add3Cards() {
if indexOfAllCards < allCards.count, gameIsStarted == true {
for _ in 0...2 {
GameBoard.allOpenCards.append(GameBoard.allCardsInDeck[indexOfAllCards])
indexOfAllCards+=1
}
}
findSet()
}
private func add12Cards() {
let numbersOfFirst12Cards = 12
if gameIsStarted == true {
for _ in 0..<numbersOfFirst12Cards {
GameBoard.allOpenCards.append(GameBoard.allCardsInDeck[indexOfAllCards])
indexOfAllCards += 1
}
}
findSet()
}
private func createDeck() {
allCards.forEach{ card in
card.isFaceUp = false
card.frame = Deck.frame
GameBoard.allCardsInDeck.append(card)
}
}
}
This is my CardBoardView where all my animations happening
import UIKit
#IBDesignable
class CardBoardView: UIView {
let durationForDisappearingCards = 2.0
let delayForDisappearingCards = 0.0
lazy var animator = UIDynamicAnimator(referenceView: self)
lazy var cardBehavior = CardBehavior(in: animator)
var allOpenCards = [CardSubview]() {
didSet { addSubview(); setNeedsLayout();}
}
var allCardsInDeck = [CardSubview] () {
didSet{ setNeedsLayout()}
}
struct Layout {
static let ratio:CGFloat = 2.0
static let insetByX:CGFloat = 2.0
static let insetByY:CGFloat = 2.0
}
private func addSubview() {
for card in allOpenCards {
addSubview(card)
}
}
public func removeSubviews() {
for card in allOpenCards {
card.removeFromSuperview()
}
}
public func removeSubviewsFromDeck() {
allCardsInDeck.forEach{ card in
card.removeFromSuperview()
}
}
public func mixCards() {
var mixCards = [CardSubview]()
for _ in 0..<allOpenCards.count {
let random = allOpenCards.count.arc4random
let card = allOpenCards[random]
mixCards.append(card)
allOpenCards.remove(at: random)
}
allOpenCards = mixCards
}
public func doWhenMatched(card: CardSubview) {
// cardBehavior.addItem(card)
UIView.animate(
withDuration: self.durationForDisappearingCards,
delay: self.delayForDisappearingCards,
animations: {
card.frame = CGRect(x: (self.superview?.frame.midX)!-card.bounds.width/2, y: (self.superview?.frame.minY)!-card.bounds.height, width: card.bounds.width , height: card.bounds.height)
}, completion: { finish in
card.removeFromSuperview()
self.allOpenCards.remove(at: self.allOpenCards.index(of: card)!)
}
)
}
// let newPosition = CGRect(x: self.frame.midX-card.bounds.width/2 , y: self.frame.minY, width: card.bounds.width, height: card.bounds.height)
//
//
//
// UIView.animate(withDuration: durationForDisappearingCards, delay: delayForDisappearingCards, options: [.allowUserInteraction], animations: {card.alpha=0.0})
override func layoutSubviews() {
super.layoutSubviews()
var grid = Grid(layout: .aspectRatio(Layout.ratio), frame: self.bounds)
grid.cellCount = allOpenCards.count
var secondsForDelay:CGFloat = 0
let secondsForFlipCard:CGFloat = 0.5
for index in allOpenCards.indices {
if self.allOpenCards[index].frame != (grid[index]?.insetBy(dx: Layout.insetByX, dy: Layout.insetByY)) ?? CGRect.zero {
UIView.animate(
withDuration: 0.5,
delay: TimeInterval(secondsForDelay) ,
animations: {self.allOpenCards[index].frame = (grid[index]?.insetBy(dx: Layout.insetByX, dy: Layout.insetByY)) ?? CGRect.zero},
completion: {
finish in
if self.allOpenCards[index].isFaceUp != true {
UIView.transition(with: self.allOpenCards[index],
duration: TimeInterval(secondsForFlipCard),
options: [.transitionFlipFromLeft],
animations: {
self.allOpenCards[index].isFaceUp = true
}
)
}
}
)
secondsForDelay+=0.02
}
}
}
}
private extension Int {
var arc4random: Int {
if self > 0 {
return Int(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -Int(arc4random_uniform(UInt32(self)))
} else {
return 0
}
}
}
private extension CGFloat {
var arc4random: CGFloat {
if self > 0 {
return CGFloat(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -CGFloat(arc4random_uniform(UInt32(self)))
} else {
return 0
}
}
}

Swift 4- App crashes on button press that starts segue

I am programming an application and it is supposed to go to another view controller on a button press but instead it crashes when I run it. The editor shows this: and with editor breakpointing on it shows this: but there is no error in the console. Here is the code for the viewcontroller that does the segue:
//
// DoTodayViewController.swift
// Paley Foundation: Starfish Support
//
// Created by Sa'ar Lipshitz on 10/28/17.
//
import UIKit
class DoTodayViewController: UIViewController {
#IBOutlet weak var navItem: UINavigationItem!
#IBOutlet weak var getBtn: UIButton!
#IBOutlet weak var suggestBtn: UIButton!
var name = "Sa'ar"
override func viewDidLoad() {
super.viewDidLoad()
getBtn.layer.cornerRadius = 8
getBtn.clipsToBounds = true
suggestBtn.layer.cornerRadius = 8
suggestBtn.clipsToBounds = true
navItem.prompt = "What would you like to do today, "+name+"?"
}
#IBAction func suggest(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "SuggestViewController") as! SuggestViewController
navigationController?.pushViewController(vc, animated: true)
}
//This crashes my app:
#IBAction func get(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "AccessViewController") as! AccessViewController
navigationController?.pushViewController(vc, animated: true)
}
}
and here is my code for the viewcontroller that gets opened:
//
// AccessViewController.swift
// Paley Foundation: Starfish Support
//
// Created by Sa'ar Lipshitz on 10/29/17.
//
import UIKit
import Firebase
import Material
class AccessViewController: UIViewController {
var adviceRef: DatabaseReference!
var advice: [[String: Any]] = []
var i = 0
var card: Card!
var loadingLbl: UILabel!
var panGesture = UIPanGestureRecognizer()
var cardOrigPos: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
adviceRef = Database.database().reference().child("Advice")
var advDict: NSDictionary?
adviceRef.observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
advDict = value
print(advDict as Any)
for (_, adv) in advDict! {
self.advice.append(adv as! [String : Any])
}
print(self.advice)
self.showNextCard()
}) { (error) in
print(error.localizedDescription)
}
let swipeRec = UIPanGestureRecognizer(target: self, action: #selector(swipe(sender:)))
card.isUserInteractionEnabled = true
card.addGestureRecognizer(swipeRec)
}
#objc func swipe(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
let velocity = sender.velocity(in: view)
let direction = velocity
print("Translation: \(translation)")
if sender.state == .began {
cardOrigPos = card.center
} else if sender.state == .changed {
card.center = CGPoint(x: cardOrigPos.x+translation.x, y: cardOrigPos.y+translation.y)
} else if sender.state == .ended {
if velocity.y == 0 && velocity.x == 0 {
UIView.animate(withDuration: 0.3, animations: {
self.card.center = self.view.center
})
} else {
UIView.animate(withDuration: 0.3, animations: {
self.card.center = CGPoint(x: self.cardOrigPos.x+direction.x*5, y: self.cardOrigPos.y+direction.y)
})
self.showNextCard()
}
}
}
func showNextCard() {
if advice.count >= 0 {
let adv = advice[i]
setCard(adv["username"] as? String, adv["title"] as! String, adv["desc"] as! String, image: adv["image"] as? UIImage)
i += 1
if i >= advice.count {
i = 0
}
}
}
func setCard(_ username: String?, _ title: String, _ desc: String, image: UIImage?) {
let toolbar = setToolbar(username ?? "", title)
card = Card()
card.toolbar = toolbar
card.toolbarEdgeInsetsPreset = .square3
card.toolbarEdgeInsets.bottom = 0
card.toolbarEdgeInsets.right = 8
card.contentView = setContentView(content: desc)
card.contentViewEdgeInsetsPreset = .wideRectangle3
card.layer.borderWidth = 1.0
card.layer.borderColor = UIColor.black.cgColor
card.layer.cornerRadius = 2
view.layout(card).horizontally(left: 20, right: 20).center()
}
func setToolbar(_ username: String, _ title: String) -> Toolbar {
let toolbar = Toolbar(rightViews: [setMoreBtn()])
toolbar.title = title
toolbar.detail = username
toolbar.detailLabel.textColor = Color.grey.base
return toolbar
}
func setMoreBtn() -> IconButton {
let moreBtn = IconButton(image: Icon.cm.moreVertical, tintColor: Color.grey.base)
return moreBtn
}
func setContentView(content: String) -> UILabel {
let contentView = UILabel()
contentView.numberOfLines = 0
contentView.text = content
contentView.font = RobotoFont.regular(with: 14)
return contentView
}
}
When I tried using print statements to debug, the one in the DoTodayViewController.get(_ sender: Any) method ran but not the one in AccessViewController.viewDidLoad()
Make sure you changed the class of the view controller object in the storyboard to AccessViewController and that the Storyboard ID is correctly spelled. You may be encountering a nil when trying to force unwrap storyboard?.instantiateViewController(withIdentifier: "AccessViewController") as! AccessViewController. Try using if let wherever an optional needs to be unwrapped to handle the scenario of it being nil.

Save highscore in swift

I gave this code, and I want to add highscore to this project. I wa trying to do this on my own but can't get this to working. Can somebody help me with achieve that ?
I was trying to manipulate with timeInterval but with no luck, this is my learning project from GitHub, I Was trying to ask author to help me but he didn't answer.
fileprivate extension ViewController {
func setupPlayerView() {
playerView.bounds.size = CGSize(width: radius * 2, height: radius * 2)
playerView.layer.cornerRadius = radius
playerView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
view.addSubview(playerView)
}
func startEnemyTimer() {
enemyTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(generateEnemy(timer:)), userInfo: nil, repeats: true)
}
func stopEnemyTimer() {
guard let enemyTimer = enemyTimer,
enemyTimer.isValid else {
return
}
enemyTimer.invalidate()
}
func startDisplayLink() {
displayLink = CADisplayLink(target: self, selector: #selector(tick(sender:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
}
func stopDisplayLink() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
displayLink = nil
}
func getRandomColor() -> UIColor {
let index = arc4random_uniform(UInt32(colors.count))
return colors[Int(index)]
}
func getEnemyDuration(enemyView: UIView) -> TimeInterval {
let dx = playerView.center.x - enemyView.center.x
let dy = playerView.center.y - enemyView.center.y
return TimeInterval(sqrt(dx * dx + dy * dy) / enemySpeed)
}
func gameOver() {
stopGame()
displayGameOverAlert()
}
func stopGame() {
stopEnemyTimer()
stopDisplayLink()
stopAnimators()
gameState = .gameOver
}
func prepareGame() {
removeEnemies()
centerPlayerView()
popPlayerView()
startLabel.isHidden = false
clockLabel.text = "00:00.000"
gameState = .ready
}
func startGame() {
startEnemyTimer()
startDisplayLink()
startLabel.isHidden = true
beginTimestamp = 0
gameState = .playing
}
func removeEnemies() {
enemyViews.forEach {
$0.removeFromSuperview()
}
enemyViews = []
}
func stopAnimators() {
playerAnimator?.stopAnimation(true)
playerAnimator = nil
enemyAnimators.forEach {
$0.stopAnimation(true)
}
enemyAnimators = []
}
func updateCountUpTimer(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
clockLabel.text = format(timeInterval: elapsedTime)
}
func highscore(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
highscoreLabel.text = format(timeInterval: elapsedTime)
}
func format(timeInterval: TimeInterval) -> String {
let interval = Int(timeInterval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let milliseconds = Int(timeInterval * 1000) % 1000
return String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
}
func checkCollision() {
enemyViews.forEach {
guard let playerFrame = playerView.layer.presentation()?.frame,
let enemyFrame = $0.layer.presentation()?.frame,
playerFrame.intersects(enemyFrame) else {
return
}
gameOver()
}
}
func movePlayer(to touchLocation: CGPoint) {
playerAnimator = UIViewPropertyAnimator(duration: playerAnimationDuration,
dampingRatio: 0.5,
animations: { [weak self] in
self?.playerView.center = touchLocation
})
playerAnimator?.startAnimation()
}
func moveEnemies(to touchLocation: CGPoint) {
for (index, enemyView) in enemyViews.enumerated() {
let duration = getEnemyDuration(enemyView: enemyView)
enemyAnimators[index] = UIViewPropertyAnimator(duration: duration,
curve: .linear,
animations: {
enemyView.center = touchLocation
})
enemyAnimators[index].startAnimation()
}
}
func displayGameOverAlert() {
let (title, message) = getGameOverTitleAndMessage()
let alert = UIAlertController(title: "Game Over", message: message, preferredStyle: .alert)
let action = UIAlertAction(title: title, style: .default,
handler: { _ in
self.prepareGame()
}
)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
func getGameOverTitleAndMessage() -> (String, String) {
let elapsedSeconds = Int(elapsedTime) % 60
switch elapsedSeconds {
case 0..<10: return ("I try again", "You need more practice")
case 10..<30: return ("Maybe Another try?", "No bad, just play more")
case 30..<60: return ("Play again", "You are like Ninja")
default:
return ("WOW", "You are simply the best")
}
}
right know I managed to have state of score but I can't to save high score whats wrong with that
func updateBest(){
var defaults=UserDefaults()
var highscore=defaults.integer(forKey: "highscore")
if(Int(elapsedTime)) > highscore
{
defaults.set(Int(self.elapsedTime), forKey: "highscore")
}
var highscoreshow=defaults.integer(forKey: "highscore")
highscoreLabel.text = "\(highscoreshow)"
print("hhScore reported: \(highscoreLabel.text)")
// clockLabel.text = "\(elapsedTime)"
// print("play reported: \(clockLabel.text)")
}
For saving highscore to userdefaults :
let highscore : String = "\(Int(self.elapsedTime))"
UserDefaults.standard.set(highscore, forKey: "highscore")
For retrieving highscore from userdefaults :
highscoreLabel.text = (UserDefaults.standard.value(forKey: "highscore") as? String)
I hope it helps. Cheers. :]

complateTransition don't dismiss the from view swift 2

I am trying to set an interactive transition with this class :
class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerInteractiveTransitioning, UIViewControllerTransitioningDelegate, UIViewControllerContextTransitioning {
weak var transitionContext: UIViewControllerContextTransitioning?
var sourceViewController: UIViewController! {
didSet {
enterPanGesture = UIScreenEdgePanGestureRecognizer()
enterPanGesture.addTarget(self, action:"panned:")
enterPanGesture.edges = UIRectEdge.Left
sourceViewController.view.addGestureRecognizer(enterPanGesture)
}
}
var togoSourceViewController: UIViewController!
let duration = 1.0
var presenting = true
var reverse = false
var originFrame = CGRectNull
var shouldBeInteractive = false
private var didStartedTransition = false
private var animated = false
private var interactive = false
private var AnimationStyle = UIModalPresentationStyle(rawValue: 1)
private var didFinishedTransition = false
private var percentTransition: CGFloat = 0.0
private var enterPanGesture: UIScreenEdgePanGestureRecognizer!
private var tovc = UIViewController()
private var pointtovc = CGPoint()
private var pointfromvc = CGPoint()
private var fromvc = UIViewController()
private var generalcontainer = UIView()
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
animated = true
let container = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
if reverse {
toViewController.view.center.x -= (container?.bounds.size.width)!
container?.insertSubview(toViewController.view, aboveSubview: fromViewController.view)
} else {
toViewController.view.center.x += (container?.bounds.size.width)!
container?.addSubview(toViewController.view)
}
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions.TransitionNone, animations: {
if self.reverse {
toViewController.view.center.x += (container?.bounds.size.width)!
} else {
toViewController.view.center.x -= (container?.bounds.size.width)!
}
}, completion: { finished in
transitionContext.completeTransition(true)
self.animated = false
self.reverse = !self.reverse
})
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return duration
}
func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning) {
interactive = true
animated = true
let container = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! //ArticleView
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! //Article OU Favoris
if reverse {
toViewController.view.frame.origin.x = -fromViewController.view.frame.maxX
container?.insertSubview(toViewController.view, aboveSubview: fromViewController.view)
}
tovc = toViewController
pointtovc = toViewController.view.bounds.origin
fromvc = fromViewController
pointfromvc = fromViewController.view.bounds.origin
generalcontainer = container!
}
func containerView() -> UIView? {
return sourceViewController?.view
}
func viewControllerForKey(key: String) -> UIViewController? {
return sourceViewController?.storyboard!.instantiateViewControllerWithIdentifier(key)
}
func viewForKey(key: String) -> UIView? {
return sourceViewController?.storyboard!.instantiateViewControllerWithIdentifier(key).view
}
func initialFrameForViewController(vc: UIViewController) -> CGRect {
return vc.view.frame
}
func finalFrameForViewController(vc: UIViewController) -> CGRect {
return vc.view.frame
}
func isAnimated() -> Bool {
return animated
}
func isInteractive() -> Bool {
return interactive
}
func presentationStyle() -> UIModalPresentationStyle {
return AnimationStyle!
}
func completeTransition(didComplete: Bool) {
interactive = false
animated = false
shouldBeInteractive = false
didFinishedTransition = didComplete
transitionContext?.finishInteractiveTransition()
transitionContext?.completeTransition(true)
}
func updateInteractiveTransition(percentComplete: CGFloat) {
if self.reverse {
print(percentComplete)
self.tovc.view.frame.origin.x = (self.fromvc.view.frame.maxX * (percentComplete)) - self.fromvc.view.frame.maxX
}
}
func finishInteractiveTransition() {
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions.TransitionNone, animations: {
if self.reverse {
self.tovc.view.frame.origin.x = self.fromvc.view.frame.origin.x
}
}, completion: { finished in
self.animated = false
self.reverse = !self.reverse
self.completeTransition(true)
})
}
func cancelInteractiveTransition() {
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions.TransitionNone, animations: {
if self.reverse {
self.tovc.view.frame.origin.x = -self.fromvc.view.frame.maxX
}
}, completion: { finished in
self.animated = false
self.completeTransition(true)
})
}
func transitionWasCancelled() -> Bool {
return didFinishedTransition
}
func targetTransform() -> CGAffineTransform {
return CGAffineTransform()
}
func completionSpeed() -> CGFloat {
return 1 - percentTransition
}
func panned(pan: UIPanGestureRecognizer) {
switch pan.state {
case .Began:
animated = true
shouldBeInteractive = true
didStartedTransition = true
didFinishedTransition = false
sourceViewController?.dismissViewControllerAnimated(true, completion: nil)
updateInteractiveTransition(0)
break
case .Changed:
percentTransition = CGFloat(pan.translationInView(sourceViewController!.view).x / sourceViewController!.view.frame.width)
if percentTransition < 0.0 {
percentTransition = 0.0
} else if percentTransition > 1.0 {
percentTransition = 1.0
}
updateInteractiveTransition(percentTransition)
break
case .Ended, .Failed, .Cancelled:
animated = false
shouldBeInteractive = false
didStartedTransition = false
didFinishedTransition = true
if percentTransition < 0.8 {
cancelInteractiveTransition()
} else {
finishInteractiveTransition()
}
break
case .Possible:
break
}
}
}
The animateTransition works perfectly and dismiss my fromViewController but during my InteractiveTransition when I call finishInteractiveTransition() and then completeTransition(true), I still have my both view :
But on Apple they said :
You must call this method after your animations have completed to notify the system that the transition animation is done. The parameter you pass must indicate whether the animations completed successfully. For interactive animations, you must call this method in addition to the finishInteractiveTransition or cancelInteractiveTransition method. The best place to call this method is in the completion block of your animations.
So, what am I doing wrong ?
I am using ios9, swift 2, Xcode 7 beta 6
I found the solution :
i should call transitionContext.completeTransition(true) in function finishInteractiveTransition() but on my previous code transitionContext was not the same that in startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning)
So i add one variable :
private var Context: UIViewControllerContextTransitioning?
and use this to call :
transitionContext.completeTransition(true)
or
transitionContext.completeTransition(false)

Resources