Updating Labels on a second View Controller - ios

I have several different ViewControllers set up in my project. The first one acts as a 'landing' page and and on pressing a button the view is directed to a following ViewController.
There are various buttons and labels on there that I want to use to provide information and run a method. The buttons all work ok, but I have followed every tutorial I can to get the labels to update based on a method, and I can't seem to get them to do so.
I know the methods are called correctly as I have put print statements in there to check.
Basic idea is, program plays a series of beeps separated by a delay (bleep_time), this changes each level (bleep_level) and there are several steps (bleep_step) in each level. Ive simplified the arrays containing this data to save space.
I have successfully created a number of tutorial projects and the labels all update correctly, but they only use one ViewController.
Is the issue I am facing due to using 2 ViewControllers?
ViewController 1
import UIKit
class Bleep_Test_Menu: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
View Controller 2
import UIKit
import AVFoundation
class Bleep_Test_Level_1: UIViewController {
// Global Variables
var audioPlayer: AVAudioPlayer?
// Bleep test delays
var bleep_time = [1,2,3,4]
// Levels of bleep test
var bleep_level = [1,2,3,4]
// Level Label
#IBOutlet weak var bleepTestLevel1Level: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// Function to run repeated bleeps
#IBAction func bleepTestLevel1Start(_ sender: Any) {
var i = 0
tracker = false
let length = bleep_time.count
while i < length {
var bleeplevel = bleep_level[i]
bleepTestLevel1Level.text = "\(bleeplevel)"
playSaveSound()
sleep(UInt32(bleep_time[i]))
i += 1
}
}
// Function to play sounds
func playSaveSound(){
let path = Bundle.main.path(forResource: "Sound1.wav", ofType: nil)!
let url = URL(fileURLWithPath: path)
do {
//create your audioPlayer in your parent class as a property
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.play()
} catch {
print("couldn't load the file")
}
}
}
The only error I keep getting in the console is this;
"Tests[53616:891478] - changing property contentsGravity in transform-only layer, will have no effect"
But I can't seem to find anything that relates to my issue.

Related

Today Widget unable to load with a specific line of code

I've added a Today extension to my app and it all works fine until a specific line of code is compiled. NB: compiled, not executed!
My TodayViewController is:
class StoredDoses {
func getDoses(doses: inout [Dose]) {
if let userD = UserDefaults(suiteName: "com.btv.mySuite") {
if let dosesData = userD.object(forKey: "doses_key") {
do {
// -----------------------------------------------
// Comment the line below out and the widget works
doses = try PropertyListDecoder().decode([Dose].self, from: dosesData as! Data)
// -----------------------------------------------
} catch {
print ("ERROR")
}
}
}
}
}
class TodayViewController: UIViewController, NCWidgetProviding {
#IBOutlet weak var aText: UILabel!
#IBOutlet weak var bText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
//Just for development stage - not real, final code
let form = DateFormatter()
form.timeStyle = .short
aText.text = form.string(from: Date())
completionHandler(NCUpdateResult.newData)
}
}
So, the above code isn't well written, but it's what I've used to finally narrow down the cause of the unloading widget. The array of Doses is a custom, codable class, but if I try to get an array of String then it's the same. The StoredDoses code is included in the main app and doesn't cause any problems.
Just to re-iterate: I'm not trying to execute any method in the StoredDoses class. I don't even have an instance of it in the widget. When the doses = ... line is merely commented out then the widget loads and the aText label in the widget appears with the current time in it.
Ok, so thanks to #Chris' apparently unconnected advise I got it sorted!
It appears to have been an Interface Builder issue: somehow it had retained the original name of the UILabel that was auto-created when I added the Today extension in Xcode. At some point, after connecting an IBOutlet to the label with "Hello World" in it, I'd renamed it to something slightly more relevant but hadn't unconnected it before over-typing the new name in the TodayViewController.
The console didn't throw up any problems and at times seemed to work, but when the line with
try PropertyListDecoder().decode([Dose].self, from: dosesData as! Data)
was present then it stopped working without any console messages.
I only found that out after I explored #Chris comment about the as! Data. I re-wrote to first get the Data:
if let userD = UserDefaults(suiteName: "com.btv.mySuite") {
if let dosesData = userD.object(forKey: "doses_key") {
if let unwrappedData = dosesData as? Data {
do {
doses = try PropertyListDecoder().decode([SplitDose].self, from: unwrappedData)
} catch {
doses.removeAll()
}
}
}
}
Once this was compiled (remember, it's still not being executed - this is just sitting there waiting to be used) the console threw up a message and the app crashed out showing the old UILabel name as not key-compliant. Reconnecting the UILabel in IB fixed everything and I could compile the original code....
This probably deserves a Radar entry but right now I don't want to waste another day re-creating (if at all possible) this problem!

How to apply multiple filter to the particular Portion of video

I want to apply filter to the video like tiktok app. for example If my video is 1 min long then I want to apply one filter till 30 second and another filter till 30 sec so I need two filter effects for 1min long video.
Using following Code I have applied one static filter effect to whole video via GPUImage
import UIKit
import GPUImage
class ViewController: UIViewController {
#IBOutlet weak var renderView: RenderView!
var movie:MovieInput!
var filter:SketchFilter!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bundleURL = Bundle.main.resourceURL!
let movieURL = URL(string:"Jelly.3gp", relativeTo:bundleURL)!
do {
movie = try MovieInput(url:movieURL, playAtActualSpeed:true)
filter = SketchFilter()
movie --> filter --> renderView
movie.runBenchmark = true
movie.start()
} catch {
print("Couldn't process movie with error: \(error)")
}
}
}
How can I get video Range or anything which help me to achieve this?

Swift in iOS with AVAudioPlayer: Singleton not working the way I hoped it would

I am using a standard Master-Detail project, listing songs in the Master and playing them in Detail. Each song has up to four parts playing simultaneously, with independent volume control, so I have four AVAudioPlayer objects in Detail, each with a slider with an IBOutlet and an IBAction to implement the volume control.
The problem is that when you click on a song (in the list on the Master), the previous song doesn't stop. Both songs play, although the volume controls now only control the most recent song. This can go on for any number of songs.
I want to get rid of the currently playing song when a new song is clicked on.
I thought I might be able to accomplish this by creating the players inside a Singleton, in such a way that there would only ever be four players. Since, according to the documentation, each player can only play one sound file at a time, I hoped the previous sound file would stop playing when the new one started. But it's not working. The same behavior described above is still happening: multiple songs can play simultaneously, with the volume controls only controlling the most recent song. Any suggestions would be greatly appreciated.
Here is the code for the Singleton:
import Foundation
import AVFoundation
class FourPlayers {
static let audioPlayers = [one, two, three, four]
static let one = AVAudioPlayer()
static let two = AVAudioPlayer()
static let three = AVAudioPlayer()
static let four = AVAudioPlayer()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
(Initially, I had just made audioPlayers static, but when that didn't work, I decided to make each individual player static as well.)
Then, in the DetailViewController:
var audioPlayers = FourPlayers.audioPlayers
Here's code for one of the four volume controls:
#IBOutlet weak var vol1: UISlider!
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[0].volume = vol1.value
}
Playing a song looks like this (the audioFiles array is populated when the song info is passed in from the Master):
var audioFiles = []
func playAudioFiles() {
var i = 0
for _ in audioFiles {
audioPlayers[i].play()
i+=1
}
}
This is the code telling the players which file to play:
func prepareAudioFiles () {
var i = 0;
for audioFile in audioFiles {
let s = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(audioFile as? String, ofType: "mp3")!)
do {
audioPlayers[i] = try AVAudioPlayer(contentsOfURL:s)
} catch {
print("Error getting the audio file")
}
audioPlayers[i].prepareToPlay()
self.audioPlayers[i].delegate = self
}
}
It appears the the volume control is only adjusting the first player (index 0)
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[0].volume = vol1.value
}
maybe you could add a variable to keep track of the currently playing index
var currentlyPlayingIndex = 0
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[currentlyPlayingIndex].volume = vol1.value
}
And update it when needed
When you play the audio you could use the same variable
func playAudioFiles() {
var i = 0
for _ in audioFiles {
if i == currentlyPlayingIndex {
audioPlayers[i].play()
} else {
audioPlayers[i].stop()
}
i+=1
}
}
The problem is the following line:
var audioPlayers = FourPlayers.audioPlayers
You are actually creating a new copy of the FourPlayers.audioPlayers array into the audioPlayers variable. So you are not reusing the AVAudioPlayers but creating independent ones for each song.
See the "Assignment and Copy Behavior for Strings, Arrays, and Dictionaries" section in the Swift Programming Documentation, Classes and Structures
You could use the static variable directly in your code instead of making a copy, for example:
#IBOutlet weak var vol1: UISlider!
#IBAction func volAdjust1(sender: AnyObject) {
FourPlayers.audioPlayers[0].volume = vol1.value
}
and:
var audioFiles = []
func playAudioFiles() {
var i = 0
for _ in audioFiles {
FourPlayers.audioPlayers[i].play()
i+=1
}
}
and update the rest of your code accordingly.

Instance member Goals cannot be used on type "ViewController" SWIFT XCODE

Hey I'm trying to Categorised or basically manually list stats in a way where i can give the program conditions to find exactly what I'm looking for so like if i was looking for how many goals where scored in the first 0 to 45 (first half) of a game that Barcelona was playing in on a specific day i could. Say in code find me all the games that were played on this date and where two or more goals were score in the first 0 to 45 minutes. As I'm not sure if i did it right but i tried to give the strings a number/Int value to represent the minute the goal was score in the game. And i also did the same with the date for the game. But the problem is there's an error at (don't Mind The spelling error lol "Barcelona")
let BarcelonavsRealMadrid1 = [Goals, Penaltys]
Instance member Goals cannot be used on type "ViewController"
also if theres a better way to do this please you can rewrite my code. Thanks
Code:
import UIKit
class ViewController: UIViewController {
let Games = [BarcelonavsRealMadrid1: 1/13/14]
let SpainPrimeraDivision = [RealMadrid, Bareclonia]
let RealMadrid = [BarcelonavsRealMadrid1: 1/12/14]
let Bareclonia = [BarcelonavsRealMadrid1: 1/12/14]
let BarcelonavsRealMadrid1 = [Goals, Penaltys]
let Goals : [String : Int] = ["BarcelonaGoal":21,"RealMadridGoal":23]
let Penaltys : [String : Int] = ["RealMadridPenalty":21,"BarcelonaPenalty":23]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You cannot reference one instance variable in the default assignment of another one. The following is the simplified version of your problem:
class ViewController: UIViewController {
let foo = 12
let bar = foo
}
Normally you have to setup the variables in the init method. But since you have a UIViewController here, you have to do it in viewDidLoad. You therefore have to write
class ViewController: UIViewController {
var foo : Int!
var bar : Int!
override func viewDidLoad() {
super.viewDidLoad()
foo = 12
bar = foo
}
}
Your code has a few more problems than that however:
you are trying to access variables before they are declared all over the place
what is 1/12/14 supposed to be? I guarantee you it is not a date
stop starting variable names with upper case letters
What you have to do:
rename all your variables
change all you variables to var and assign value to them in the correct order inside viewDidLoad
deal with the date situation
Your issue is that you are making references between class properties during initialization - this isn't permitted. One way around this is to set some of the things initially, then in viewDidLoad or in viewWillAppear set the dependencies, like this:
//-------All Games Ever played------------------------------------
var BarcelonavsRealMadrid1 = [[String : Int]]() // if you want this globally
let Goals : [String : Int] = ["BarcelonaGoal":21,"RealMadridGoal":23]
let Penaltys : [String : Int] = ["RealMadridPenalty":21,"BarcelonaPenalty":23]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
BarcelonavsRealMadrid1 = [Goals, Penaltys]
}
also there were few more problems suggested by #luk2302 in his answer
Try this
var BarcelonavsRealMadrid1:[[String:Int]] { return [Goals, Penaltys] }

Flow Control in Swift - AVAudioPlayer

I wrote an Iphone Swift app that plays a series of sounds in a random order using AVAudioPlayer-- for now a pop sound, a horn sound and a gong. It works when you hit the play button, except....
However, when I hit the stop button nothing happens- it doesn't respond (The stop does work if I have just one sound). I believe it is due to my flow control. If I did not put in the 'while soundPlayer.playing == true {}', the code would "fly" through the sound and not wait for it to finish.
How can I modify the code so the the sound plays to completion before going to the next sound? And also allow the stop button to be functional? See Code and screen shot below. (Actually Stack overflow will not allow me to post an image since I am so new)
//
// ViewController.swift
// InsultSchool2 Swift
//
// Created by JR on 12/3/14.
// Copyright (c) 2014 JR. All rights reserved.
//
import UIKit
import AVFoundation
//---------------------------------------------------
var soundPlayer:AVAudioPlayer = AVAudioPlayer()
// used to try to do some kind of flow control. May not be needed if better way is found.
var stopFlag:Bool = false
//-----------------------------------------------------
class ViewController: UIViewController {
#IBOutlet weak var valueSliderTime: UISlider!
#IBOutlet weak var valueLabelTime: UILabel!
#IBOutlet weak var valueStepperVolume: UIStepper!
#IBOutlet weak var valueLabelVolume: UILabel!
//------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Displays an initial Volume value in volume label on load
//Convert to an int. Otherwise you get a weird value when getting close to zero
//Multiply by 10 so the int works. Otherwise you would int'ing a number between 0.0 and 1.0.
// "\()" is a shorthand to convert whatever to a string
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//------------------------------------------------------
#IBAction func buttonStop(sender: UIBarButtonItem) {
NSLog("Enter Button Stop")
//?1 If a sound is not playing and stop is hit, then it crashes
//?2 the stop button does not work with the whlle loop below
soundPlayer.stop()
}
#IBAction func sliderTime(sender: UISlider) {
valueLabelTime.text = "\(Int(valueSliderTime.value))"
}
#IBAction func stepperVolume(sender: UIStepper) {
//Converted to an int. Otherwise you get a weird value when getting close to zero
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
#IBAction func buttonPlay(sender: UIBarButtonItem) {
NSLog("Enter Button Start")
var soundArray:[String] = ["Sound0", "Sound1", "Sound2"]
// Randomizes a number to indicate which random sound to play in the array
/* Number is from 0 to number in the (). Don't add one or 0 will never play. Go one more than the numbers in the array. For example if you have 3 items in the array go to 3. THis will go from 0 to 2 (ie., 3 items)*/
// Reference----- var soundRandomNumber:Int = Int(arc4random_uniform(3))
var soundRandomNumber:Int
soundRandomNumber = Int(arc4random_uniform(3))
//Creates a random number to wait between sounds based on the slider value.
//arc4random requires a UInt32 (Unsigned is a positive number).
//_uniform is slightly more random than without the Uniform
//The documentation says to use Int otherwise.
println(Int(valueSliderTime.value))
NSLog("valueSliderTime.value")
var waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
println(waitTimeRandomNumber)
NSLog("waitTimeRandomNumber")
// Constructs a string with the random number for the URL
//var soundFile:String = soundArray[soundRandomNumber]
var soundFile:String
soundFile = soundArray[soundRandomNumber]
//Reference---- var soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
var soundURL:NSURL!
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
//?3 How do I set up a loop or control that works until the stop button is pressed?
while stopFlag == false{
NSLog("inside while")
println(stopFlag)
//?4 Is the below correct? The actual volume does not seem to change though the .volume does
soundPlayer.volume = Float(valueStepperVolume.value)
println(Float(valueStepperVolume.value))
NSLog("Float(valueStepperVolume.value)")
println(soundPlayer.volume)
NSLog("soundPlayer.volume")
soundRandomNumber = Int(arc4random_uniform(3))
soundFile = soundArray[soundRandomNumber]
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
soundPlayer.prepareToPlay()
soundPlayer.play()
//?5 How do I make the player not blow through the sound and wait until is finished
while soundPlayer.playing == true {
}
//?6 How can i make a random timer that waits for a random time before relooping?
waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
}// End of while loop
} //ends playButton IBAction
//?7 How to allow this app to play over other music in another player
}
use the repeat while statement instead to control the flow:
here is a link to apple's developers reference:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
if ++diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")

Resources