我正在尝试关注YouTube教程并通过制作简单的纸牌游戏来学习Swift。完成本教程后,我想添加一些我自己的功能,例如在应用程序打开后重置乐谱和卡片。当我说打开时,我的意思是进入前台或启动应用程序。我目前正在尝试此操作,方法是在reset
中创建一个名为ViewController.swift
的方法,并在调用函数AppDelegate.swift
时调用applicationWillEnterForeground
中的方法。我能够成功构建代码并运行它,但是当应用程序到达前台时,我收到一条错误,指出“线程1:致命错误:在解开可选值时意外发现了nil”。
AppDelegate.swift:
//
// AppDelegate.swift
// War
//
// Created by Rafael on 3/17/18.
// Copyright © 2018 Rafael. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{
var window: UIWindow?
var ViewControl: ViewController = ViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
print("Launching app!")
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
print("Going inactive!")
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
print("Entering background!")
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
print("Entering Foreground!")
ViewControl.reset()
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
print("Going active!")
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
print("Going to terminate!")
}
}
enter code here
ViewController.swift:
//
// ViewController.swift
// War
//
// Created by Rafael on 3/17/18.
// Copyright © 2018 Rafael. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var rightImageView: UIImageView!
@IBOutlet weak var leftImageView: UIImageView!
@IBOutlet weak var leftScoreLabel: UILabel!
var leftScore = 0
@IBOutlet weak var rightScoreLabel: UILabel!
var rightScore = 0
let cardNames = ["card2", "card3", "card4", "card5", "card6", "card7", "card8", "card9", "card10", "card11", "card12", "card13", "card14"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
print("loading view!")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func dealTapped(_ sender: Any) {
let leftNumber = Int(arc4random_uniform(13))
let rightNumber = Int(arc4random_uniform(13))
if leftNumber > rightNumber {
leftScore += 1
leftScoreLabel.text = String(leftScore)
} else if(rightNumber == leftNumber){
// Do nothing - TIE
} else{
rightScore += 1
rightScoreLabel.text = String(rightScore)
}
leftImageView.image = UIImage(named: cardNames[leftNumber])
rightImageView.image = UIImage(named: cardNames[rightNumber])
}
func reset() {
print("resetting!")
leftImageView.image = UIImage(named: "back")
rightImageView.image = UIImage(named: "back")
rightScoreLabel.text = "0"
leftScoreLabel.text = "0"
rightScore = 0
leftScore = 0
}
}
所有网点都运行良好,应用程序运行正常。唯一的问题是调用函数reset
时。当应用程序即将进入视图时,如何让我的应用程序重置卡片?我还在学习这门语言。
答案 0 :(得分:2)
这里有几点需要考虑。首先,尝试重置您的视图状态,每次他们离开应用程序并返回时几乎肯定不是用户期望的。你能想象玩游戏会是什么样的,一封电子邮件进来然后你切换到外观,然后回去,你的游戏又回到了关卡的开头?
此外,除非您正在执行独立于View Controller的操作,例如从后台获取云记录,否则您可能需要远离AppDelegate。 Apple为其平台设置的范例(模型视图控制器)是从视图控制器内部控制您在屏幕上看到的内容。您正试图从AppDelegate控制View Controller,以便操纵屏幕上的内容。我建议重新考虑你如何控制屏幕内容。此外,一旦应用程序在后台运行一段时间(您在主屏幕或其他应用程序中),它最终将重置其状态,然后将再次调用viewWillAppear。这就是为什么每个ViewController都有保存和恢复状态的方法,当发生这种情况时。
尽管如此,有一种方法可以从AppDelegate访问ViewController的实例。您可以像self.radius
一样访问根视图控制器。但请记住,您认为主屏幕可能不是根视图控制器。根控制器可以是标签栏或导航控制器。如果是这样,你必须继续沿着链条走下去,直到找到它为止。
将消息传递给ViewController的另一种方法是在没有直接访问它的实例的情况下,使用基础的NotificationCenter发送消息。像这样:let rootVC = UIApplication.shared.keyWindow?.rootViewController
。然后在视图控制器中你会输入这样的内容:NotificationCenter.default.post(name: NSNotification.Name(rawValue: "app has returned from background"), object: nil)
,然后创建该函数。我认为iOS有应用程序进入后台的内置通知,你可以设置观察。如果不查看文档,我就不确定它们是什么。
答案 1 :(得分:1)
你做错了一些事。首先,您似乎没有将任何数据保存到磁盘,因此无论如何,每次启动时一切都会重置。但这里有几件事。
1:您应该在视图控制器本身内重置ViewController的属性/状态;不是来自AppDelegate。
2:你将这个视图控制器变量命名为。
var ViewController: ViewController = ViewController()
。在Swift / Objective-C中命名变量的正确方法是驼峰var viewController = ViewController()
或更好,但更像var myViewController = ViewController()
。因为当您在ViewController中键入时,它与类本身具有相同的名称,因此它并未尝试访问您的实例。你自己(以及编译器)将你的ivars命名为与类名完全相同是非常困惑的。
3:您初始化了一个ViewController实例,但它与您在屏幕上看到的不一样。您在屏幕上看到的内容由Storyboard拥有并创建。你在AppDelegate中创建的ViewController实际上并没有做任何事情。这就是IBOutlets为零的原因,因为屏幕上的标签位于Storyboard自己制作的实例内部。因此,您所要做的就是将您的逻辑放在ViewController.swift文件中;你不必创建自己的实例。
话虽这么说,如果你想在屏幕上出现视图控制器之后发生某些事情,你需要在其中编写一个名为override func viewWillAppear (animated: Bool) {}
的函数或者将它放在viewDidLoad()中。在iOS中,使用模型视图控制器模式。每个屏幕都由UIViewController控制。因此,任何用于操作屏幕的逻辑及其视图都应该在View Controller子类内部完成。视图控制器的工作方式是,首先调用viewDidLoad()
,然后调用viewWillAppear()
,最后调用viewDidAppear()
。