Code refactoring - Swift - swift

I think my code regarding buttons is violating the DRY principle. Can you suggest a more efficient way to reduce the clutter?
#IBAction func competitiveButton(_ sender: Any) {
if competitiveMatch.isEnabled == true {
competitiveMatch.isEnabled = false
friendlyMatch.isEnabled = true
tournamentButton.isEnabled = true
trainingButton.isEnabled = true
} else {
competitiveMatch.isEnabled = true
}
}
#IBAction func friendlyButton(_ sender: Any) {
if friendlyMatch.isEnabled == true {
friendlyMatch.isEnabled = false
tournamentButton.isEnabled = true
competitiveMatch.isEnabled = true
trainingButton.isEnabled = true
} else {
friendlyMatch.isEnabled = true
}
}
#IBAction func tourneyButton(_ sender: Any) {
if tournamentButton.isEnabled == true {
tournamentButton.isEnabled = false
friendlyMatch.isEnabled = true
trainingButton.isEnabled = true
competitiveMatch.isEnabled = true
} else {
tournamentButton.isEnabled = true
}
}
#IBAction func trainingButton(_ sender: Any) {
if trainingButton.isEnabled == true {
trainingButton.isEnabled = false
friendlyMatch.isEnabled = true
tournamentButton.isEnabled = true
competitiveMatch.isEnabled = true
} else {
trainingButton.isEnabled = true
}
}
The code follows a set pattern for all four buttons. I am not able to factor a shorter method to do the same. Please help. Complete beginner here.

You can use a single IBAction instead of 4 separate methods. And make the sender type to UIButton from Any. Then you can do something like
#IBAction func toggleEnableDisable(_ sender: UIButton) {
let currentValue = sender.isEnabled
let buttonArray = [trainingButton, friendlyMatch, tournamentButton, competitiveMatch]
buttonArray.forEach { $0.isEnabled = false }
sender.isEnabled = !currentValue
}

Related

Array Issue with Display Label

Everything appears to be working in this tic tac toe game, except for displaying the "draw" label when there is no winner. The label will switch when Cross or circles wins, but not when there is a tie.
I'm stumped. E
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var count = 1
var activePlayer = 1 //Cross
var gameState = [0,0,0,0,0,0,0,0,0]
var gameIsActive = true
let winningCombinations = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]]
#IBAction func action(_ sender: AnyObject) {
if (gameState[sender.tag-1] == 0 && gameIsActive == true) {
gameState[sender.tag-1] = activePlayer
if (activePlayer == 1) {
sender.setImage(UIImage(named: "cross.png"), for: UIControl.State())
activePlayer = 2
} else {
sender.setImage(UIImage(named: "nought.png"), for: UIControl.State())
activePlayer = 1
}
}
for combination in winningCombinations {
if gameState[combination[0]] != 0 &&
gameState[combination[0]] == gameState[combination[1]] &&
gameState[combination[1]] == gameState[combination[2]] {
gameIsActive = false
if gameState[combination[0]] == 1 {
label.text = "Cross has won!"
} else {
label.text = "Circle has won!"
}
if gameIsActive == true {
for i in gameState {
count = i*count
}
if count != 0 {
label.text = "It was a draw."
label.isHidden = false
playAgainButton.isHidden = false
}
}
playAgainButton.isHidden = false
label.isHidden = false
}
}
} // End Button Action
#IBOutlet weak var playAgainButton: UIButton!
#IBAction func playAgain(_ sender: Any) {
gameState = [0,0,0,0,0,0,0,0,0]
gameIsActive = true
activePlayer = 1
playAgainButton.isHidden = true
label.isHidden = true
for i in 1...9 {
let button = view.viewWithTag(i) as! UIButton
button.setImage(nil, for: UIControl.State())
}
}
override func viewDidLoad() {
super.viewDidLoad()
playAgainButton.isHidden = true
label.isHidden = true
}
} // End ViewController
You are saying
gameIsActive = false
// ... some other stuff ...
if gameIsActive == true {
// check for a draw
}
But gameIsActive is not true, because you just set it to false. Therefore we never perform the check for a draw.
Thanks for the help everyone! I finally got it working correctly.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var playAgainButton: UIButton!
#IBOutlet weak var label: UILabel!
var count = 1
var activePlayer = 1 //Cross
var gameState = [0,0,0,0,0,0,0,0,0]
var gameIsActive = true
let winningCombinations = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]]
#IBAction func action(_ sender: AnyObject) {
count = 1;
if (gameState[sender.tag-1] == 0 && gameIsActive == true) {
if (activePlayer == 1) {
gameState[sender.tag-1] = activePlayer
sender.setImage(UIImage(named: "cross.png"), for: UIControl.State())
activePlayer = 2
} else {
sender.setImage(UIImage(named: "nought.png"), for: UIControl.State())
gameState[sender.tag-1] = activePlayer
activePlayer = 1
}
}
for combination in winningCombinations {
if gameState[combination[0]] != 0 && gameState[combination[0]] ==
gameState[combination[1]] && gameState[combination[1]] ==
gameState[combination[2]]{
gameIsActive = false
if gameState[combination[0]] == 1 {
label.text = "Cross has won!"
} else {
label.text = "Circle has won!"
}
playAgainButton.isHidden = false
label.isHidden = false
}
}
if gameIsActive == true{
for i in gameState{
count = i * count
}
if count != 0{
label.text = "DRAW!"
playAgainButton.isHidden = false
label.isHidden = false
}
}
}
#IBAction func playAgain(_ sender: Any) {
gameState = [0,0,0,0,0,0,0,0,0]
gameIsActive = true
activePlayer = 1
playAgainButton.isHidden = true
label.isHidden = true
for i in 1...9
{
let button = view.viewWithTag(i) as! UIButton
button.setImage(nil, for: UIControl.State())
}
}
override func viewDidLoad() {
super.viewDidLoad()
playAgainButton.isHidden = true
label.isHidden = true
}
}

the function nextTapped does not move to next pdf page

function nextTapped and function previousTapped show this error " Use of unresolved identifier 'pdfView'; did you mean 'PDFView'? " I want from function nextTapped move to next pdf page and function previousTapped move to previous pdf page when i display pdf i want from nextButton move to next pdf page and previousButton move to previous pdf page
import UIKit
import AVFoundation
import MobileCoreServices
import PDFKit
class RecorderViewController: UIViewController {
#IBOutlet var nextButton:UIButton!
#IBOutlet var previousButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
nextButton.isHidden = true
previousButton.isHidden = true
}
#IBAction func `import`(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
documentPicker.delegate = self as? UIDocumentPickerDelegate
documentPicker.allowsMultipleSelection = true
present(documentPicker, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextTapped(_ sender: Any) {
pdfView.goToNextPage(sender)}
#IBAction func previousTapped(_ sender: Any) {
pdfView.goToPreviousPage(sender)
}
extension RecorderViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let selectedFileURL = urls.first else {
return
}
/////////her to display PDF File
let pdfView = PDFView()
pdfView.frame = CGRect(x: 0, y: 160, width: 1024, height: 1139)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
let thumbnailView = PDFThumbnailView()
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)
thumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
thumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
thumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: thumbnailView.topAnchor).isActive = true
pdfView.heightAnchor.constraint(equalToConstant: 150)
pdfView.displayMode = .singlePage
// Fit content in PDFView.
nextButton.isHidden = false
previousButton.isHidden = false
pdfView.autoScales = true
pdfView.document = PDFDocument(url: selectedFileURL)
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sandboxFileURL = dir.appendingPathComponent(selectedFileURL.lastPathComponent)
///i need to ensure the file is saved
if FileManager.default.fileExists(atPath: sandboxFileURL.path) {
print("Already exists! Do nothing")
}
else {
do {
try FileManager.default.copyItem(at: selectedFileURL, to: sandboxFileURL)
print("Copied file!")
}
catch {
print("Error: \(error)")
}
}
}
}
Define your pdfView in Class it self not in Extension like below :
//Add PDF View Object here not in extension
let pdfView = PDFView()
Just update your code slightly like this :
import UIKit
import AVFoundation
import MobileCoreServices
import PDFKit
class RecorderViewController: UIViewController {
//Add PDF View Object here not in extension
let pdfView = PDFView()
#IBOutlet var nextButton:UIButton!
#IBOutlet var previousButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
nextButton.isHidden = true
previousButton.isHidden = true
}
#IBAction func `import`(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
documentPicker.delegate = self as? UIDocumentPickerDelegate
documentPicker.allowsMultipleSelection = true
present(documentPicker, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextTapped(_ sender: Any) {
pdfView.goToNextPage(sender)
}
#IBAction func previousTapped(_ sender: Any) {
pdfView.goToPreviousPage(sender)
}
}
extension RecorderViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let selectedFileURL = urls.first else {
return
}
/////////her to display PDF File
pdfView.frame = CGRect(x: 0, y: 160, width: self.view.frame.size.width, height: self.view.frame.size.height - 160)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
let thumbnailView = PDFThumbnailView()
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)
thumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
thumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
thumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: thumbnailView.topAnchor).isActive = true
pdfView.heightAnchor.constraint(equalToConstant: 150)
pdfView.displayMode = .singlePage
// Fit content in PDFView.
nextButton.isHidden = false
previousButton.isHidden = false
pdfView.autoScales = true
pdfView.document = PDFDocument(url: selectedFileURL)
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sandboxFileURL = dir.appendingPathComponent(selectedFileURL.lastPathComponent)
///i need to ensure the file is saved
if FileManager.default.fileExists(atPath: sandboxFileURL.path) {
print("Already exists! Do nothing")
}
else {
do {
try FileManager.default.copyItem(at: selectedFileURL, to: sandboxFileURL)
print("Copied file!")
}
catch {
print("Error: \(error)")
}
}
}
Hope this will solve your issue.

Timer Going too fast after first quiz Swift 3

i have quiz app and have 10 sec timer each question. First question timer is fine, but in second question it going faster than before. I try this tutorial to make timer and this tutorial to make quiz app
here's my code :
override func viewDidLoad() {
timerBegin()
}
func timerBegin() {
myTimer.invalidate() // print error `fatal error: unexpectedly found nil while unwrapping an Optional value`
timeLeft = 10
myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(SoalViewController.timerRunning), userInfo: nil, repeats: true)
}
func timerRunning() {
timeLeft -= 1
timeLabel.text = "\(timeLeft)"
if timeLeft == 0 {
myTimer.invalidate()
timeLabel.text = "10"
pickQuestion()
timerBegin()
}
}
here's my option button code :
#IBAction func opsiA(_ sender: Any) {
timeLabel.text = "10"
timerBegin()
if AnswerNumber == 0 {
pickQuestion()
Score += 10
scoreLabel.text = "\(Score)"
} else {
pickQuestion()
print("Wrong")
}
}
#IBAction func opsiB(_ sender: Any) {
timeLabel.text = "10"
timerBegin()
if AnswerNumber == 1 {
pickQuestion()
Score += 10
scoreLabel.text = "\(Score)"
} else {
pickQuestion()
print("Wrong")
}
}
#IBAction func opsiC(_ sender: Any) {
timeLabel.text = "10"
timerBegin()
if AnswerNumber == 2 {
pickQuestion()
Score += 10
scoreLabel.text = "\(Score)"
} else {
pickQuestion()
print("Wrong")
}
}
#IBAction func opsiD(_ sender: Any) {
timeLabel.text = "10"
timerBegin()
if AnswerNumber == 3 {
pickQuestion()
Score += 10
scoreLabel.text = "\(Score)"
} else {
pickQuestion()
print("Wrong")
}
}
and this is my pick question function :
func pickQuestion() {
if Questions.count > 10 {
QNumber = Int(arc4random_uniform(UInt32(10)))
textView.text = Questions[QNumber].Question
AnswerNumber = Questions[QNumber].Answer
for i in 0..<Buttons.count {
Buttons[i].setTitle(Questions[QNumber].Answers[i], for: UIControlState.normal)
}
Questions.remove(at: QNumber)
} else {
darkView.isHidden = false
scoreLabel.text = "\(Score)"
timeLabel.isHidden = true
if Score > standStill {
HighScore = Score
highScoreLabel.text = NSString(format: "Highscore : %i", HighScore) as String
let currentHighScore = UserDefaults.standard
currentHighScore.set(HighScore, forKey: "HighScore")
} else {
let currentHighScore = UserDefaults.standard
currentHighScore.set(standStill, forKey: "HighScore")
}
}
}
i already try to stop timer by adding myTimer.invalidate() in first line of timerBegin function but it print fatal error: unexpectedly found nil while unwrapping an Optional value

NSOpenPanel as sheet

Ive looked around at other answers, but nothing seems to be helping my case.
I have a viewController class which contains an IBAction for a button. This button should open a NSOpenPanel as a sheet from that viewController:
class ViewController: NSViewController {
#IBAction func folderSelection(sender: AnyObject) {
var myFiledialog: NSOpenPanel = NSOpenPanel()
myFiledialog.prompt = "Select path"
myFiledialog.worksWhenModal = true
myFiledialog.allowsMultipleSelection = false
myFiledialog.canChooseDirectories = true
myFiledialog.canChooseFiles = false
myFiledialog.resolvesAliases = true
//myFiledialog.runModal()
myFiledialog.beginSheetModalForWindow(self.view.window!, completionHandler: nil)
var chosenpath = myFiledialog.URL
if (chosenpath!= nil)
{
var TheFile = chosenpath!.absoluteString!
println(TheFile)
//do something with TheFile
}
else
{
println("nothing chosen")
}
}
}
The problem comes from myFileDialog.beginSheetModalForWindow(..) , it works with the line above, but that is not a sheet effect
You need to call beginSheetModalForWindow from your panel on your window, and use a completion block:
let myFiledialog = NSOpenPanel()
myFiledialog.prompt = "Select path"
myFiledialog.worksWhenModal = true
myFiledialog.allowsMultipleSelection = false
myFiledialog.canChooseDirectories = true
myFiledialog.canChooseFiles = false
myFiledialog.resolvesAliases = true
myFiledialog.beginSheetModalForWindow(window, completionHandler: { num in
if num == NSModalResponseOK {
let path = myFiledialog.URL
print(path)
} else {
print("nothing chosen")
}
})
Swift 5
let dialog = NSOpenPanel()
dialog.beginSheetModal(for: self.view.window!){ result in
if result == .OK, let url = dialog.url {
print("Got", url)
}
}

Sound through UIButton does not work, swift

I have 4 buttons that trigger 4 different sounds. All buttons work, I have a println button 1, button2, etc.. For some reason I am only hearing sounds that come through button 3 and 4. I used the same coding method for all buttons and audio players, So I have no idea what the problem could be. This is my code.
There are no errors or anything and again all buttons and audio players work the same, so I'm not sure why button 1 and 2 don't work.
Any help would be much appreciated.
class MusicPlayer {
var onePlayer : AVAudioPlayer?
var twoPlayer : AVAudioPlayer?
var threePlayer : AVAudioPlayer?
var fourPlayer : AVAudioPlayer?
func playOneSound() {
twoPlayer!.play()
}
func playTwoSound() {
twoPlayer!.play()
}
func playThreeSound() {
threePlayer!.play()
}
func playFourSound() {
fourPlayer!.play()
}
func stopOneSound() {
twoPlayer!.stop()
}
func stopTwoSound() {
twoPlayer!.stop()
}
func stopThreeSound() {
threePlayer!.stop()
}
func stopFourSound() {
fourPlayer!.stop()
}
class var sharedInstance: MusicPlayer {
struct Static {
static var instance: MusicPlayer?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = MusicPlayer()
}
return Static.instance!
}
init() {
// Create an NSURL for the correct file
let oneSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Go To Sleep 1", ofType: "mp3")!)
let twoSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Go To Sleep 2", ofType: "mp3")!)
let threeSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Go To Sleep 3", ofType: "mp3")!)
let fourSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Go To Sleep 5", ofType: "mp3")!)
// Create a new AVAudioPlayer with the correct file
onePlayer = AVAudioPlayer(contentsOfURL: oneSound, error: nil)
twoPlayer = AVAudioPlayer(contentsOfURL: twoSound, error: nil)
threePlayer = AVAudioPlayer(contentsOfURL: threeSound, error: nil)
fourPlayer = AVAudioPlayer(contentsOfURL: fourSound, error: nil)
/*onePlayer!.volume = masterEffectSoundLevel
twoPlayer!.volume = masterEffectSoundLevel
threePlayer!.volume = masterEffectSoundLevel
fourPlayer!.volume = masterEffectSoundLevel*/
// Prepare for playback, set loop count to infinite, start playback
onePlayer!.prepareToPlay()
twoPlayer!.prepareToPlay()
threePlayer!.prepareToPlay()
fourPlayer!.prepareToPlay()
}
This is my code for the buttons :
#IBAction func settingOneButton(sender: UIButton) {
settingOne.hidden = false
settingTwo.hidden = true
settingThree.hidden = true
settingFour.hidden = true
}
#IBAction func settingTwoButton(sender: UIButton) {
settingOne.hidden = true
settingTwo.hidden = false
settingThree.hidden = true
settingFour.hidden = true
}
#IBAction func settingThreeButton(sender: UIButton) {
settingOne.hidden = true
settingTwo.hidden = true
settingThree.hidden = false
settingFour.hidden = true
}
#IBAction func settingFourButton(sender: UIButton) {
settingOne.hidden = true
settingTwo.hidden = true
settingThree.hidden = true
settingFour.hidden = false
}
#IBAction func goToSleepPressed(sender: AnyObject) {
if settingOne.hidden == false {
MusicPlayer.sharedInstance.playOneSound()
MusicPlayer.sharedInstance.stopTwoSound()
MusicPlayer.sharedInstance.stopThreeSound()
MusicPlayer.sharedInstance.stopFourSound()
println("one")
} else if settingTwo.hidden == false {
MusicPlayer.sharedInstance.playTwoSound()
MusicPlayer.sharedInstance.stopOneSound()
MusicPlayer.sharedInstance.stopThreeSound()
MusicPlayer.sharedInstance.stopFourSound()
println("two")
} else if settingThree.hidden == false {
MusicPlayer.sharedInstance.playThreeSound()
MusicPlayer.sharedInstance.stopOneSound()
MusicPlayer.sharedInstance.stopTwoSound()
MusicPlayer.sharedInstance.stopFourSound()
println("three")
} else if settingFour.hidden == false {
MusicPlayer.sharedInstance.playFourSound()
MusicPlayer.sharedInstance.stopOneSound()
MusicPlayer.sharedInstance.stopTwoSound()
MusicPlayer.sharedInstance.stopThreeSound()
println("four")
}
}
I figured out what the problem was. I had twoPlayer both in playOneSound() and playTwoSound().