EDIT
Full code https://forums.raywenderlich.com/t/swiftui-getting-started-raywenderlich-com/73445/11?u=prashantkt
I am creating a demo in SwiftUI to match the colors from https://www.raywenderlich.com/3715234-swiftui-getting-started
I have done some experiments on that demo , Please let me know if you need more code or information :)
This is how looks like on first run
On Match me button tapped I compute the score
and show alert and rest the slider value,
Button(action: {
self.score = self.computeScore()
self.resetTheColor()
self.needToShoWAlert = true
}) {
Text("Match me")
}
func resetTheColor () {
rTarget = Double.random(in: 0..<1)
gTarget = Double.random(in: 0..<1)
bTarget = Double.random(in: 0..<1)
sliderBinder.reset()
}
And Here is sliderBinder class
class SliderBindable:BindableObject {
var r:Double = 0 {
didSet {
didChange.send((r,g,b))
}
}
var g:Double = 0 {
didSet {
didChange.send((r,g,b))
}
}
var b:Double = 0 {
didSet {
didChange.send((r,g,b))
}
}
var didChange = PassthroughSubject<(r:Double,g:Double,b:Double),Never>()
func reset() {
r = 0
g = 0
b = 0
}
}
Label shrinks when rest the r,g,b value
see the screen shot
Related
I am working my way through the Stanford Spring 2020 iOS course.
I got this error in my code: Use of unresolved identifier 'isMatched' (Actually Xcode displays this same error two times).
Please help me :( I can't see anything wrong in my code. Here's my code:
import Foundation
import SwiftUI
struct MemoryGame<CardContent> where CardContent: Equatable {
var cards: Array<Card>
var theme: Theme //Assignment 2
var score: Int = 0
var indexOfOneAndOnlyFaceUpCard: Int? {
get { cards.indices.filter { cards[$0].isFaceUp }.only }
set {
for index in cards.indices { // sets all other than current "indexOfOneAndOnlyFaceUpCard" cards to isFaceUp = false
cards[index].isFaceUp = index == newValue
}
}
}
mutating func choose(card: Card) {
if let chosenIndex = cards.firstIndex(matching: card), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched { //if chosen card is not faceUp and not yet matched
if let potentialMatchIndex = indexOfOneAndOnlyFaceUpCard { // if one card is already face up
if cards[chosenIndex].content == cards[potentialMatchIndex].content { // if already faceUp card = chosen card
cards[chosenIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
score += 2
} else /* if both cards not the same */ {
//if card.isSeen {score -= 1} // and the currently choosen one isSeen - my bad solution
}
self.cards[chosenIndex].isFaceUp = true
} else { // if this (currently choosen card) is only faceUp card
indexOfOneAndOnlyFaceUpCard = chosenIndex
}
}
// Assignment 2 - probably good fragment
if card.isSeen == false {
if let chosenIndex = cards.firstIndex(matching: card) {
cards[chosenIndex].isSeen = true
}
}
}
init(numberOfPairsOfCards: Int, theme: Theme, cardContentFactory: (Int) -> CardContent) {// Assignment 2 theme: Theme
cards = Array<Card>()
for pairIndex in 0..<numberOfPairsOfCards {
let content = cardContentFactory(pairIndex)
cards.append(Card(content: content, id: pairIndex*2))
cards.append(Card(content: content, id: pairIndex*2+1))
}
cards.shuffle()
self.theme = theme
}
struct Card: Identifiable {
var isFaceUp: Bool = false
var isMatched: Bool = false
var isSeen: Bool = false
var content: CardContent
var id: Int
}
// Assignment 2:
struct Theme {
let name: String
let emojis: [CardContent]
let numberOfCardPairsToShow: Int?
let color: Color //import swiftui
init(name: String, emojis: [CardContent], numberOfCardPairsToShow: Int? = nil, color: Color) {
self.name = name
self.emojis = emojis
self.numberOfCardPairsToShow = numberOfCardPairsToShow
self.color = color
}
}
}
The places where you have cards[chosenIndex].isMatched = true and cards[potentialMatchIndex].isMatched = true could be the issue.
The array[index] returns an optional. This is because the index could be invalid.
You need to unwrap the value before you can access isMatched.
Like so :
if let chosenCard = cards[chosenIndex] as? Card {
chosenCard.isMatched = true
}
I am adding in some functionalities for this iOS swift matching card game and I need to check if the first 2 buttons flipped over match. They have four types of emojis for eight cards, which means there are 4 pairs of matches. I am having trouble finding out how to check if the cards match and when they match, I need the background color of the buttons to opaque (invisible). Everything else works except the empty if statement in the concentration class within the chooseCard function. That is where I need help.
Here's all the code so you can see whats related to what:
class ViewController: UIViewController {
lazy var game = Concentration(numberOfPairsOfCards: (cardButtons.count + 1) / 2)
var flipCount = 0 {
// Property Observer
didSet { flipLabel.text = "Flips: \(flipCount)" }
}
#IBOutlet weak var flipLabel: UILabel!
#IBOutlet var cardButtons: [UIButton]!
#IBAction func touchCard(_ sender: UIButton) {
flipCount+=1
if let cardNumber = cardButtons.firstIndex(of: sender) {
game.chooseCard(at: cardNumber)
updateViewFromModel()
} else {
print ("chosen card was not in cardButtons")
}
}
var emojiChoices = ["👻", "🎃", "🙏🏾", "🦆"]
var emoji = [Int:String]()
func emoji(for card: Card) -> String {
if emoji[card.identifier] == nil, emojiChoices.count > 0 {
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)
}
return emoji[card.identifier] ?? "?"
}
func updateViewFromModel() {
for index in cardButtons.indices {
let button = cardButtons[index]
let card = game.cards[index]
if card.isFaceUp {
button.setTitle(emoji(for: card), for: UIControl.State.normal)
button.backgroundColor = UIColor.white
}
else {
button.setTitle("", for: UIControl.State.normal)
button.backgroundColor = UIColor.orange
}
}
}
}
import Foundation
class Concentration {
var cards = [Card]()
func chooseCard(at index: Int) {
if cards[index].isFaceUp {
cards[index].isFaceUp = false
}
else {
cards[index].isFaceUp = true
}
if cards[index].isFaceUp && cards[index].isMatched {
}
}
init(numberOfPairsOfCards: Int) {
for _ in 0..<numberOfPairsOfCards {
let card = Card()
cards += [card,card]
}
//TODO: Shuffle cards
cards.shuffle()
}
}
import Foundation
struct Card {
var isMatched = false
var isFaceUp = false
var identifier: Int
static var identifierFactory = 0
static func getUniqueIdentifier() -> Int {
Card.identifierFactory += 1
return Card.identifierFactory
}
init() {
self.identifier = Card.getUniqueIdentifier()
}
}
In your concentration class you will check for faceUp card indexes
Change your Concentration from class to Struct
struct Concentration { // instead of class Concentration
private var indexOfFaceUpCard: Int? {
get {
let faceUpCardIndices = cards.indices.filter { cards[$0].isFaceUp }
return faceUpCardIndices.count == 1 ? faceUpCardIndices.first : nil
} set {
for index in cards.indices {
cards[index].isFaceUp = (index == newValue)
}
}
}
Then you have mutating chooseCard method
mutating func chooseCard(at index: Int)
In your chooseCard method you check for matching
if !cards[index].isMatched {
if let matchIndex = indexOfFaceUpCard, matchIndex != index {
if cards[matchIndex] == cards[index] {
cards[matchIndex].isMatched = true
cards[index].isMatched = true
}
cards[index].isFaceUp = true
} else {
indexOfFaceUpCard = index
}
}
So your method look like this
mutating func chooseCard(at index: Int) {
if !cards[index].isMatched {
if let matchIndex = indexOfFaceUpCard, matchIndex != index {
if cards[matchIndex] == cards[index] {
cards[matchIndex].isMatched = true
cards[index].isMatched = true
}
cards[index].isFaceUp = true
} else {
indexOfFaceUpCard = index
}
}
}
Update your card struct
struct Card: Hashable {
var isMatched = false
var isFaceUp = false
var identifier: Int
static var identifierFactory = 0
static func getUniqueIdentifier() -> Int {
Card.identifierFactory += 1
return Card.identifierFactory
}
static func ==(lhs: Card, rhs: Card) -> Bool {
return lhs.identifier == rhs.identifier
}
init() {
self.identifier = Card.getUniqueIdentifier()
}
}
I am trying to detect the percentage of time that the Main thread is busy so I can log this metric when a user is using the app. Currently, the closest thing I can find is the user_time from basic_info with the help from an answer here but how can i know what thread is being used and if its the main thread? Then from here how can i tell how much of that time as a percentage of the apps total run time for that session?
So i managed to figure this out using the following:
class MainThreadAnalyser {
typealias SumTotal = (total: Double, elements: Double)
private var threadPercentages = [Double]()
private let timeIntervalSeconds: Double
init(timeIntervalSeconds: Double) {
self.timeIntervalSeconds = timeIntervalSeconds
if #available(iOS 10.0, *) {
self.startCPUMonitoring(timeIntervalSeconds: self.timeIntervalSeconds)
}
}
func getAverageCpuUsage() -> Double? {
let sumTotal = threadPercentages.reduce((total: 0, elements: 0)) { (sum, item) -> SumTotal in
var result = sum
if item > 0 {
result.total += item
result.elements += 1
}
return result
}
return sumTotal.elements > 0 ? sumTotal.total / sumTotal.elements : nil
}
#available(iOS 10.0, *)
private func startCPUMonitoring(timeIntervalSeconds: Double) {
Timer.scheduledTimer(withTimeInterval: timeIntervalSeconds, repeats: true) { [weak self] _ in
guard let strongSelf = self else { return }
if let cpuUsage = strongSelf.cpuUsage() {
strongSelf.threadPercentages.append(cpuUsage)
}
}
}
private func cpuUsage() -> Double? {
var kernReturn: kern_return_t
var taskInfoCount: mach_msg_type_number_t
taskInfoCount = mach_msg_type_number_t(TASK_INFO_MAX)
var tinfo = [integer_t](repeating: 0, count: Int(taskInfoCount))
kernReturn = task_info(mach_task_self_, task_flavor_t(TASK_BASIC_INFO), &tinfo, &taskInfoCount)
if kernReturn != KERN_SUCCESS {
return -1
}
var threadArray: thread_act_array_t? = UnsafeMutablePointer(mutating: [thread_act_t]())
var threadCount: mach_msg_type_number_t = 0
defer {
if let threadArray = threadArray {
vm_deallocate(mach_task_self_, vm_address_t(UnsafePointer(threadArray).pointee), vm_size_t(threadCount))
}
}
kernReturn = task_threads(mach_task_self_, &threadArray, &threadCount)
if kernReturn != KERN_SUCCESS {
return -1
}
var totalCPU: Double?
if let threadArray = threadArray {
for index in 0 ..< Int(threadCount) {
var threadInfoCount = mach_msg_type_number_t(THREAD_INFO_MAX)
var thinfo = [integer_t](repeating: 0, count: Int(threadInfoCount))
kernReturn = thread_info(threadArray[index], thread_flavor_t(THREAD_BASIC_INFO),
&thinfo, &threadInfoCount)
if kernReturn != KERN_SUCCESS {
return -1
}
if index == 0 {
let cpuUse = thinfo[4]
totalCPU = Double(cpuUse/10)
}
}
}
return totalCPU
}
}
The main thread is being used constantly while the app is in the foreground.
So you can schedule a timer in applicationDidBecomeActive and invalidate in applicationWillResignActive and get the accumulated seconds whenever you want.
I make a text player which subsequently displays the phrase in one language after some time, an English translation with the phrase playing out loud using mp3. There are buttons play, pause, next, previous.
I wrote the code, there are some bugs in it:
class Sentence: Codable {
var id: Int
var rus: String
var eng: String
var link: String
init(id: Int, rus: String, eng: String, link: String) {
self.id = id
self.rus = rus
self.eng = eng
self.link = link
}
}
class WordsViewController: UIViewController {
var sentences: [Sentence] = [Sentence]()
var isPlayTouch = false
var isIncrement = false
var speed: Double = 1.5
var position: Int = 0
#objc func playAction() {
self.isPlayTouch = true
if sentences.count > 0 {
let duration = self.getDurationAudio(link: self.sentences[self.position].link)
DispatchQueue.global().async {
if self.isPlayTouch {
Thread.sleep(forTimeInterval: duration * self.speed)
if self.isPlayTouch {
DispatchQueue.main.async {
self.playSound()
self.wordsView.sentence.text = self.sentences[self.position].eng
DispatchQueue.global().async {
if self.isPlayTouch {
Thread.sleep(forTimeInterval: duration)
DispatchQueue.main.async {
if self.isPlayTouch {
if self.position == self.sentences.count - 1 {
self.pauseAction()
if !self.isIncrement {
self.isIncrement = true
}
} else {
if self.isPlayTouch {
Thread.sleep(forTimeInterval: 0.5)
if self.isPlayTouch {
self.position = self.position + 1
self.setText()
if self.isPlayTouch {
self.playAction()
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
After pressing the pause button for the delay time, and immediately pressing the playlist, two phrases are played at once, this is due to checking on the isPlayTouch variable
Tell me how to write this functionality?
I want to save data into realm if the user clicks on a button. Moreover the view should be updated if the user clicks that button. I have the following code:
#IBAction func saveAction(_ sender: UIButton) {
if currentLogWeightTextField.text!.isEmpty || currentLogRepsTextField.text!.isEmpty || currentLogRPETextField.text!.isEmpty {
errorLabel.isHidden = false
return
}
else{
if let weight = Float(currentLogWeightTextField.text!), let reps = Int(currentLogRepsTextField.text!), let rpe = Float(currentLogRPETextField.text!){
errorLabel.isHidden = true
let setToSave = excercisesFromPlan![excerciseCounter].sets[setCounter]
do{
try realm.write{
setToSave.weight = weight
setToSave.repeats = reps
setToSave.rpe = rpe
}
}
catch{
print(error)
}
}
else{
errorLabel.isHidden = false
return
}
if setCounter < excercisesFromPlan![excerciseCounter].sets.count{
setCounter += 1
setupLabels()
print(setCounter)
print(excercisesFromPlan![excerciseCounter].sets.count)
}
else{
let finished = plan!.excercises.count - 1
if excerciseCounter == finished{
performSegue(withIdentifier: SegueIdentifier.finishedWorkout, sender: nil)
return
}
else{
excerciseCounter += 1
setCounter = 1
setupLabels()
}
}
}
}
This is my setupLabel method:
func setupLabels(){
if let excercise = excercisesFromPlan?[excerciseCounter]{
excerciseNameLabel.text = "\(excercise.name)"
setsNumberLabel.text = "\(setCounter)/\(excercise.sets.count)"
}
}
These are the relevant properties:
var excercisesFromPlan: List<Excercise>?
var plan: TrainingPlan?
var excerciseCounter = 0
var setCounter = 1
excercisesFromPlan = plan?.excercises
The plan property is given through a segue.
These are my model classes:
class TrainingPlan: Object {
dynamic var trainingPlanID = NSUUID().uuidString
dynamic var routine: Routine?
dynamic var workout: Workout?
dynamic var selected = false
dynamic var name = ""
dynamic var trainingPlanDescription = ""
dynamic var creationDate = Date(timeIntervalSince1970: 1)
dynamic var lastChangeDate = Date(timeIntervalSince1970: 1)
dynamic var lastUsed = Date(timeIntervalSince1970: 1)
let excercises = List<Excercise>()
override class func primaryKey() ->String?{
return "trainingPlanID"
}
}
class Excercise: Object {
dynamic var excerciseID = NSUUID().uuidString
dynamic var trainingsplan: TrainingPlan?
dynamic var selected = false
dynamic var name = ""
dynamic var excerciseDescription = ""
dynamic var muscleGroup = ""
dynamic var record = 0
dynamic var picture: NSData?
dynamic var copied = false
let sets = List<TrainingSet>()
override class func primaryKey() ->String?{
return "excerciseID"
}
override static func indexedProperties() -> [String] {
return ["name"]
}
}
I have the problem that if I use this code and click the save button, the Labels are updating right and the counter are also working. The only problem is that the else statement
else{
let finished = plan!.excercises.count - 1
if excerciseCounter == finished{
performSegue(withIdentifier: SegueIdentifier.finishedWorkout, sender: nil)
return
}
else{
excerciseCounter += 1
setCounter = 1
setupLabels()
}
}
is never been called. I'm searching for the problem for a few hours now but I can't find it..
Strangely, if I comment out the following from the save function it works perfectly and the else statement is called right:
if let weight = Float(currentLogWeightTextField.text!), let reps = Int(currentLogRepsTextField.text!), let rpe = Float(currentLogRPETextField.text!){
errorLabel.isHidden = true
let setToSave = excercisesFromPlan![excerciseCounter].sets[setCounter]
do{
try realm.write{
setToSave.weight = weight
setToSave.repeats = reps
setToSave.rpe = rpe
}
}
catch{
print(error)
}
}
else{
errorLabel.isHidden = false
return
}
Sorry for so much code.. Does anyone know why this is not working? Thanks in advance!