Presenting ViewController anew does not re-initialize it - ios

I have a viewController (videocallVC) that I want to initialize every time it comes into view/loads.
Currently, the videocallVC only initialize the first time. If I leave the videocallVC, go to another viewController and come back to the videocallVC it holds the last session in memory and does not "refresh".
How can I make sure that every time I present the videocallVC it is initialized anew ?
import OpenTok
class videocallVC: UIViewController, OTSessionDelegate, OTSubscriberKitDelegate, OTPublisherDelegate {
#IBOutlet var subscribeView: UIView!
#IBOutlet var publishView: UIView!
let apiKey = "xxxxxxx"
var session : OTSession?
var publisher : OTPublisher?
var subscriber : OTSubscriber?
var connection : OTConnection?
override func viewDidLoad() {
super.viewDidLoad()
session = OTSession(
apiKey: apiKey,
sessionId: variableInstance.sessionID,
delegate: self)
}
override func viewWillAppear(animated: Bool) {
doConnect()
}
// MARK: - OpenTok Methods
/**
* Asynchronously begins the session connect process. Some time later, we will
* expect a delegate method to call us back with the results of this action.
*/
func doConnect() {
if let session = self.session {
var maybeError : OTError?
session.connectWithToken(variableInstance.tokboxToken, error: &maybeError)
if let error = maybeError {
showAlert(error.localizedDescription)
}
}
}
/**
* Sets up an instance of OTPublisher to use with this session. OTPubilsher
* binds to the device camera and microphone, and will provide A/V streams
* to the OpenTok session.
*/
func doPublish() {
publisher = OTPublisher(delegate: self)
var maybeError : OTError?
session?.publish(publisher, error: &maybeError)
if let error = maybeError {
showAlert(error.localizedDescription)
}
view.addSubview(publisher!.view)
publisher!.view.frame = publishView.frame
}
There is more code to the videocallVC but I gues this is sufficient to understand the problem.
Any help would be very much appreciated ! Thank you.

The method viewDidLoad is only called once. The method viewWillAppear is called every time the view will appear. So if you need to do things every time the view will appear, move it to that method (you may also use viewDidAppear if the UI behaviour makes sense). This may apply to the session = OTSession( ... ) snippet.
It may also be possible, that in your code things get initialized, then stored in a variable and later, if the variable is not nil no new initialization is done. You may solve that by setting those variables to nil in viewWillAppear.
However, without knowing all details, this may be a likely solution but still a shot in the dark :-)

how are your delegates store in OTSession and OTPublisher?
Are they weak?
If they are not weak, the View Controller can not be deinitialized, because there are still references to it. Maybe this can cause your problems.

Related

AVAudioFoundation issues when volume property is set

I am writing a class which will be initialized for background audio.
`class BackgroundAudio: NSObject,AVAudioPlayerDelegate {
var audioPlayer:AVAudioPlayer
override init() {
audioPlayer = AVAudioPlayer()
super.init()
}
There is a function that mute the background audio in the class:
func mute() {
audioPlayer.setVolume(0, fadeDuration: 2)
}
Because a few View Controller in this project may need to control the volume, so I put the initialization in the AppDelegate file like this:
var backgroundAudioPlayer = BackgroundAudio()
There is an issue when I mute() is called
I have been debugging this a bit time, getting stuck at the moment. How to resolve this issue? Thanks in advance.
I’m positive that u can do this in the info setting rather than writing code....

Error on Static Array in Swift Class

I am developing an app using swift. I create a subclass from SCNNode for this:
class Charge: SCNNode {
static var tagCounter = 0 //to give every charge a unique tag
static var charges = [Charge]() //to have a pointer that can access all charges
static var selectedCharge: Charge? //pointer to selected charge
override init() {
super.init()
super.geometry = Charge.carbonAtom()
Charge.tagCounter++
self.chargeTag = Charge.tagCounter
Charge.charges.append(self)
}
}
Then after initiating the class several times in ViewController, I want to access them by using Charge.charges. But for some unknown reason, only the last instance is available, and it occupies Charge.charges[0].
I tried to track the object movement by adding property in ViewController var test = [Charge]() and call test.append(charge) every time I initiate charge in ViewController. Then, when I want to access all the charges, Charge.charges loses most of its charges but test does not! Can anyone enlighten me on this? Or is it a bug in Xcode?
n.b. I use debugging tool to track this problem. It turns out that Charge.charges loses its first content as soon as the second initialization is finished, but the first content still exists right after the execution of Charge.charges.append(self)
edit: carbonAtom function
class func carbonAtom() -> SCNGeometry {
let carbonAtom = SCNSphere(radius: 0.8)
carbonAtom.firstMaterial!.diffuse.contents = UIColor.redColor()
carbonAtom.firstMaterial!.specular.contents = UIColor.whiteColor()
return carbonAtom
}
I have just tested, there is not any Xcode bug.
class Charge: NSObject {
static var tagCounter = 0 //to give every charge a unique tag
static var charges = [Charge]() //to have a pointer that can access all charges
override init() {
super.init()
// super.geometry = Charge.carbonAtom()
Charge.tagCounter++
// self.chargeTag = Charge.tagCounter
Charge.charges.append(self)
}
}
Create 3 Changes instance:
for var i = 0; i < 3; i++ {
_ = Charge()
}
print(Charge.charges.count)
The console prints 3.
Try to check your Charge.carbonAtom() static method. I doubt it clear value of the charges array.
OKAY GUYS I FOUND IT!
There are infos that I dont provide (I thought it was irrelevant) in Charge class:
override func removeFromParentNode() {
super.removeFromParentNode()
for var i = 0; i<Charge.charges.count; i++ {
let charge = Charge.charges[i]
if Charge.selectedCharge != nil {
if charge == Charge.selectedCharge! {
Charge.charges.removeAtIndex(i)
}
}
break
}
}
and in ViewController
#IBAction func addCharge(sender: AnyObject) {
let charge = Charge()
scene.rootNode.addChildNode(charge) //root of problem
Charge.selectedCharge = charge
print(Charge.charges.count)
}
what happen is, in the line scene.rootNode.addChildNode(charge), the method automatically calls removeFromParentNode() from charge. Because of that, the charge pointed by Charge.selectedCharge will be removed from Charge.charges when the second charge is initialized.
I try to search for this info in Apple documentation but no avail. If only they document it more comprehensively :/
Thank you for the help guys :) the divide and conquer method I use to replicate the bug really helps narrowing down the problem

Delegate Error in Swift

I am trying to send a double value from a UIView (which is loaded from a XIB) to a ViewController using a delegate
Here is my protocol, it is just sending a double to the main ViewController on a button click.
protocol HoursWorkedDelegate{
func sendHoursWorked(hoursWorked: Double);
}
class HoursWorkedView: UIView{
var delegate: HoursWorkedDelegate?;
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
delegate!.sendHoursWorked(hoursWorked);
}
}
// This class now wants that Double value
class ViewController: UIViewController, HoursWorkedDelegate{
// Conform to protocol
func sendHoursWorked(hoursWorked: Double){
// Lets say we just want to assign this value to a textField
hoursWorkedTextField.text = NSString(format: "%.4f", hoursWorked);
}
}
The error message I get is Thread 1: EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode=0x0)
Any help would be much appreciated, Thank You!
As a start, change the exclamation point in this snippet to a question mark:
delegate!.sendHoursWorked(hoursWorked);
This is what's likely causing the crash, as you are force-unwrapping the optional delegate property. A question mark means we'll only call sendHoursWorked() on the delegate if the delegate exists.
That fix will now probably mean that your program is no longer crashing, but you still don't get the desired results, because sendHoursWorked() is never called. We have to tell our HoursWorkedView object who is delegating it.
Somewhere in your code, you might have something like this:
let hoursWorkedView = HoursWorkedView()
self.view.addSubview(hoursWorkedView)
It's right here where we should be setting the delegate:
let hoursWorkedView = HoursWorkedView()
hoursWorkedView.delegate = self
self.view.addSubview(hoursWorkedView)
Though if it's me, I probably add a constructor to HoursWorkedView that accepts the delegate property:
init(delegate: HoursWorkedDelegate) {
super.init()
self.delegate = delegate
}
And now we can just do this:
let hoursWorkedView = HoursWorkedView(delegate: self)
self.view.addSubview(hoursWorkedView)
I think you're getting your view and your viewcontroller mixed up: a ViewController controls things; a view just displays them. The viewController tells the view what to display.
So, you want to connect your button to the viewController -- not the view. And you don't need a custom view class or a delegate.
Set it up like this:
create a textField and a button
create an outlet for the textField
put calculateHoursWorked directly in your viewController
create an action to connect the button to calculateHoursWorked
in calculateHoursWorked, set self.textField.text to the result of the calculation (where "textField" is whatever you named your outlet)
You wouldn't use a delegate in this context because the viewController knows everything the view does. The delegate pattern is for cases where one object has no visibility into another.
EDIT:
That being said, the bug here is that the delegate isn't actually being set anywhere.
Swift Optionals (the ! and ?) help prevent cases like this. If you explicitly unwrap an optional using !, you have to make sure it's always defined. In this case, since delegate is defined as optional (?) you have to check it:
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
if let currentDelegate = self.delegate {
currentDelegate.sendHoursWorked(hoursWorked)
}
}

UITapGestureRecognizer Causing Crash With Differing Error Messages

I have a major issue that I am stuck with at work and would REALLY appreciate some help on. This has cost me 2 days already.
What I am trying to do is have a special image class fire it's assigned callback when it's touched. Thats it.
But when I touch the image it crashes, often without an error message just (lldb). Sometimes it says garbage like "[__NSCFData tapped:]: unrecognized selector sent to instance 0x1780af360". Sometimes it says "message sent to deallocated object".
I can run the app 10 times in a row and get one of these random messages just from tapping the same object on the screen each time.
The code is very simple:
//view controller
var k:K_PreviewImage!
override func viewDidLoad()
{
var image:iImage = iImage(imageName: "ja3.jpg")
k = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
And here is the code inside K_PreviewImage (my clickable image). This does NOT inherit from anything, including NSObject
var touchCallback:((K_PreviewImage)->Void)
{
set{
if(_touchCallback == nil)
{
var tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action:"tapped:")
_image.addGestureRecognizer(tap)
}
_touchCallback = newValue
}
get{
return _touchCallback
}
func tapped(tap:UITapGestureRecognizer)
{
println("here")
if(_touchCallback != nil)
{
touchCallback(self)
}
}
The above code causes a crash 100% of the time.
If I add #objc too the tap event listener in the K_PreviewImage like this
#objc func tapped(tap:UITapGestureRecognizer)
{
println("here")
if(_touchCallback != nil)
{
touchCallback(self)
}
}
Then the code WORKS and the touch event is fired (both inside K_PreviewImage and the controllers callback function 'nestedTap'.
Not sure why that works, but it does. However I'm still up a creek without a paddle because when I make the K_PreviewImage a function variable instead of a class member variable, I get a crash again
override func viewDidLoad()
{
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
}
So my first question is why did I need to add #objc to get the callback to fire instead of giving me an unclear 'deallocated' memory crash?
My second question is why is it when I moved the variable from a member variable to a function variable does it cause the crash all over again. If the object was deallocated it couldnt hear the tap event in the first place could it? Why would it still be on screen? And why is it getting dealloacted just because it's not a member variable!
How the heck can I create a custom object dynamically and have it fire click events!
UPDATE
Here is the full code block involving how my K_PreviewImage was being added with a touch event
var _array:Array<iImage>!
override func viewDidLoad()
{
_array = Array<iImage>()
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
_array.append(k.image)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
Solved:
override func viewDidLoad()
{
_array = Array<K_PreviewImage>()
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
_array.append(k)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
Because I was not storing a reference to the K_PreviewImage 'k' even though the subview was added, even though it belongs to k, k was not being retained. By making the array store a reference to K_PreviewImage instead of iImage the compiler now retains a reference to the K_Preview Image
First off, it looks like you're trying to add an image as a subview. I don't know Swift all that well, but _view.addSubview(k.image) looks wrong to me and would make more sense as _view.addSubview(k).
Secondly, you're not keeping your reference to your K_PreviewImage. You are assigning k.image as the subview, which causes k to be detected as "no longer used" by the system. It will clean it up and when you try to access it, you'll crash.

Accessing an IBOutlet from another class

I am new to Swift/iOS, so please bear with me:
I am trying to access a function in one class from another class, and update an UIImage name.
Within my viewcontroller class I have
class Documents: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
override func viewDidLoad() {
super.viewDidLoad()
UpdateImage()
}
func UpdateImage() {
UpdateImage.image = UIImage(named: "NewImage")
}
}
Everything works, the Image gets updated to "NewImage"
Question: I can access the UpdateImage func from another class, but why is it generating an error when trying to change the image in the Documents class?
class GetChanges {
var success = { operation:AFHTTPRequestOperation!, response:AnyObject!) -> Void in
var MakeChange = Documents()
MakeChange.UpdateImage()
}
}
This generates an error on the "UpdateImage.image = UIImage(named: "NewImage")" in the Documents Class; "fatal error: unexpectedly found nil while unwrapping an Optional value"
When you call it within the class itself, it is operating on itself and it has already been created from a nib/storyboard. This means that UpdateImage exists.
When you call the method from another class, when you call this line:
var MakeChange = Documents()
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage. Because this value doesn't exist, it unexpectedly finds nil and throws an error.
You need to somehow retain a reference to the instance of Documents you're trying to display. I'd need more information to tell you how to do that.
Also, because you mentioned that you're new, I'd like to point out a few issues I notice with your code that is making it very difficult to read.
Capitalized names are reserved for Types variable names should (almost) never begin with a capital letter.
Variable names should reflect the object they represent. UpdateImage sounds like it is an image. It would be better to name this updateImageView
Functions should be lowercase as well. It is strange to see capitalization this way and makes the code a bit uncomfortable to read.
Good luck!
Read about View Contoller's lifecycle, it's very important knowledge for iOS developer.
As Logan said:
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage
This means that after call init for ViewController (i.e. Documents()) nib isn't loaded. You can use outlets of viewController in another code only after viewDidLoad stage. Apple docs:
The nib file you specify is not loaded right away. It is loaded the first time the view controller's view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad() method and perform your tasks there.
You can remove MakeChange.UpdateImage(), because it will be called in viewDidLoad. Or, if you want pass specific image name to view controller:
class Documents: UIViewController, UITableViewDataSource,
UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
var imageName: String?
override func viewDidLoad() {
super.viewDidLoad()
updateImageView()
}
func updateImageView() {
if let imageName = imageName {
UpdateImage.image = UIImage(named: imageName)
}
}
}
After that, you can use
let documentsViewController = Documents
documentsViewController.imageName = "newImage"
When you load documentsViewController, newImage will be presented

Resources