Changing label in other view in swift - swift

I have a label in a second viewController in Swift, and I want change this between my firstViewController. I try this with prepareForSegue: also with ChildView and ParentView and accessing to label since parentView.. But I get error..
What is the correct form to can make this?
Try declared secondVIew:
class ViewController: UIViewController {
var v = View2Controller()
#IBAction func but(sender : AnyObject) {
v.label2.text = "newText" //Here get the error EXC_BAD_INSTRUCTION
}
...
class View2Controller: UIViewController {
#IBOutlet var label2 : UILabel
Thanks!

The more code you provide the easier it is to get answers.
In your case, you are initializing a band new View2Controller. Since label2 is an IBOutlet it expects data from a nib file. Since it didn't get any of this data, label2 is going to be nil hence why you get a EXC_BAD_INSTRUCTION crash.
You can access the root view controller of a navigation controller because navigation controllers are special in that they have their own stack and maintain their own view hierarchy. This is why you have to push and pop view controllers in a navigation controller. This also allows child controllers to maintain a reference to its parent controller.
The proper solution for your situation would be to use protocols. Otherwise give View2Controller a property and reference to ViewController then make changes to ViewController through that property.

Related

How to access storyboard items without #IBOutlets?

I am currently working in a IOS app where I have created a storyboard and some viewControllers.
Because many of the areas are similar I choose to create a generic viewController and then populate it with a controller that extends UIViewController.
Since I cannot add two different classes to a viewController in interface builder, I cannot link the viewController to two different controllers.
What would be the best way to accomplish such task?
Having a controller linked with the storyboard viewController and this controller handles every view that needs to be instantiated?
You can always use tags in Interface Builder for your controls.
For instance, let say you set a label with a tag of 100, and a button with a tag of 110.
var label : UILabel! { view.viewWithTag(100) as? UILabel }
var button : UIButton! { view.viewWithTag(110) as? UIButton }
So now, you can do things like:
label.text = "Yeppee!"

Weak var outlet is lost (=nil) when referred to in a delegate method

I have a UICollectionView in my class declared as
#IBOutlet weak var artworkCollectionView: UICollectionView!
Inside this class there is one delegate method called by two other View Controllers, one of these VC is a pop up, the other one is a normal VC.
The delegate method gets some data from the database and then updates the collection view calling inside a closure:
self.artworkCollectionView.reloadData()
When the delegate method is called by the pop up VC, then all works great. BUT when the delegate method is called by the normal VC when it gets to self.artworkCollectionView.reloadData() it gets the infamous Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
I have checked all the references to the cell reuseIdentifier and all is correct. I suspect that since the UICollectionView is declared as weak var, when I go from the current class to the pop up and then the pop up calls the delegate methods, the reference is not lost, but when I go from the current class to the normal VC and then the normal VC calls the delegate method the reference to my weak var is lost and so it is "seen" as nil.
#IBOutlet weak var artworkCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Set up
artworkCollectionView.dataSource = self
artworkCollectionView.delegate = self
artworkCollectionView.isUserInteractionEnabled = true
artworkCollectionView.allowsSelection = true
artworkCollectionView.register(UINib(nibName:
"MyCollectionViewCell", bundle: nil),
forCellWithReuseIdentifier: "cell")
}
// delegate method
func reloadCollections() {
retrieveAlbumRatings { (isAlbum) in
if isAlbum {
self.retrieveAlbumData(completion: { (isFinished) in
if isFinished {
// Reload collection views
self.artworkCollectionView.reloadData()
}
})
}
}
}
If I am right, my question is: how can I give weak var artworkCollectionView: UICollectionView! a STRONG reference so that it does not get lost in the flow from the current class to the normal VC and back?
EDIT: here is what I tried so far:
Remove “weak” from the outlet declaration so making it: #IBOutlet var artworkCollectionView: UICollectionView!
But I got the same error
I passed artworkCollectionView to the normal VC via override performSegue and then passed it back as an argument of the delegate method. This does not give me the fatal error but also it does not reload the UICollectionView because I think that anyway the weak reference to the UICollectionView outlet is lost.
Thanks for your help (disclaimer: I am pretty new to Swift..)
Inside this class there is one delegate method called by two other
View Controllers, one of these VC is a pop up, the other one is a
normal VC.
The delegate method gets some data from the database and then updates
the collection view calling inside a closure:
self.artworkCollectionView.reloadData()
The flow appears to be that you have a VC containing the code above, the VC can either open a pop-up or just do a standard push segue to the "normal VC".
You want some operation to occur in either the pop-up VC or normal VC, load some data and then when the user is directed back to the originating VC, the UICollectionView is updated with that data.
Your problems are the following:
I passed artworkCollectionView to the normal VC via override
performSegue and then passed it back as an argument of the delegate
method. This does not give me the fatal error but also it does not
reload the UICollectionView because I think that anyway the weak
reference to the UICollectionView outlet is lost.
You shouldn't be passing anything around like this in most cases unless you have a really good reason to do so (I don't see one).
You want a separation of concerns here. You have to think carefully about what you wanjt to pass between VCs to avoid making weird dependencies between them. I wouldn't pass outlets for multiple reasons, the first being that you now have to keep track of the outlet in multiple VCs if you ever decide to change it. The second being that it requires too much mental gymnastics to keep track of the state of the outlet since it's being passed around everywhere. The outlets are also only guaranteed to be set at certain phases of the lifecycle. For example if you retrieve the destination VC from the segue in prepareForSegue:sender: and attempt to reference the outlets at that time, they will all be nil because they haven't been set yet.
These are all good reasons why the VC that contains the code above should be the one (and the only one) maintaining control over what gets displayed in artworkCollectionView and when. The problem here is how you're approaching this, rather than having the pop-up or normal VC call the delegate method or doing weird things like passing outlets from one VC to another, just pass the data around instead.
The simplest example is:
The pop-up VC and normal VC call some code to actually fetch the
data.
Then depending on how you actually segued to the pop-up VC or
normal VC from original VC, use either parentViewController or
presentingViewController to get the reference to the original VC.
Set the data into the original VC through that reference.
Dismiss the pop-up VC or normal VC if necessary (depends on your specific app, maybe you want the user to push a UIButton to dismiss instead of doing it for them).
When the original VC comes back into view, add some code to a lifecycle method like
viewWillAppear to have it load the contents of the data into the
UICollectionView at that time.
I see no reason why you should be passing any outlets outside of the original VC that should be the one managing it.
Short answer: Don't do that. You should treat a view controller's views as private. You should add a method to your view controller that other objects call to tell it to reload it's collection view.
The longer answer is that your view controller's collection view should stick around as long as the view controller is on-screen. It sounds like you don't have a very strong understanding of object lifecycle and how ARC works. You should read up on that and do some exercises until you understand it better.
Try something like this:
//Make artworkCollectionView a normal weak var, not implicitly unwrapped.
//You'll need to change your other code to unwrap it every time you use it.
#IBOutlet weak var artworkCollectionView: UICollectionView?
...
func reloadCollections() {
retrieveAlbumRatings { (isAlbum) in
if isAlbum {
//The construct `[weak self]` below is called a capture list
self.retrieveAlbumData(completion: { [weak self] (isFinished) in
guard let weakSelf = self else {
print("self is nil");
return
}
}
if isFinished {
// Reload collection views
guard let collectionView = weakSelf.artworkCollectionView else {
print("collectionView is nil!")
return
}
collectionView.reloadData()
})
}
}
}

Loading Xib on UIView causing all the other components inside UIViewController hide

Informations :
I am trying to swap two .xib which will be inside UIView of my StepsUIViewController class. And I set their file owner name as my StepsUIViewController because I want to do their procedures inside that view controller and that is the important thing at this topic.
Requirement :
My requirement is changing the two xib base on each situation. So,
since I set their file owner name as StepsUIViewController,
implementation of both xib will be done inside StepsUIViewController.
And I am going to present that StepsUIViewController modally. So there will be no UINavigationBar until I drag and drop from tools.
Here is my view hierachy :
And here is my StepsUIViewController :
So inside Step1View, I will change the two xib I created base on push(UIButton) action. Now here comes the problem. When I load those xib, UINavigationBar didn't appear as well as push button also disappear and instead that xib(UIView) cover the whole StepsUIViewController.
Here is my code :
import UIKit
class StepsViewController: UIViewController{
#IBOutlet weak var step1View : UIView!
override func loadView() {
super.loadView()
let someView = NSBundle.mainBundle().loadNibNamed("StepsInfo", owner: self, options:nil)[0]
step1View = someView as! UIView
}
}
Here what I am facing :
What am I doing wrong? I am new to xib and all my life dropping UIComponents to UIViewController inside storyboard and I am thinking to change it and create view dynamically on single UIViewController. Any Help?

Setting SplitViewItem's ViewController

Im having trouble setting a nssplitviewcontroller's split view's view controller. I have a reference from the story board and am trying to set the items view controller programmatically:
override func viewDidLoad() {
dash = storyBoard.instantiateControllerWithIdentifier("dash_viewcontroller") as? NSViewController
print(dash)
main_view.viewController = dash!
}
I get this error from the console(doesn't crash) and doesn't show the programmatically set vc:
2016-02-21 10:03:19.475 HealthDash[62950:3960447] Failed to set (contentViewController) user defined inspected property on (NSWindow): Cannot remove a SplitViewItem's viewController if it is currently in a SplitViewController
Looks like the splitViewItem has a content controller that is actively being displayed. My guess: first you will have to remove that view controller from screen before you can replace it. Probably easier to create a new NSSplitItemView, add that to the NSSplitViewController and remove unwanted NSSplitItemView (and their associated view controllers).

How can I call this function?

I am so stuck right now, I have tried everything which seams logical to me to make this work but having no luck...
I got this in a separate swift file:
import Foundation
import UIKit
class MyViewController: UIViewController {
func background() {
var imageView : UIImageView
imageView = UIImageView(frame:CGRectMake(0, 0, 100, 300));
imageView.image = UIImage(named:"bg.jpg")
self.view.addSubview(imageView)
}
}
I want to call it into a separate view controller file. The reason I am doing it like this is because then I can just call the background class in all my view controllers a and I don't have to do the same code in each one.
The ways I have tried calling it are:
MyViewController.background() - I get error Missing parameter for #1 in call
background() - I get error Use of unresolved identifier 'background'
MyViewController() - I don't get error but nothing happens.
I would really appreciate it if someone could tell me how I can call this function into my 'ViewDidLoad' part in the view controller.
Thank you.
You're barking up the wrong tree. Even if you could call the background method in MyViewController, this would still not accomplish what you are trying to accomplish, because when you call background in the "separate view controller file", the self in the background method would be the other view controller - and so you would be putting the UIImageView into the other view controller's view, not this view controller's view. If you want to be able to do the same thing separately in each view controller, you need to make the background method available internally in each of them.
The way to do that is to inject background through an extension into UIViewController itself, from which all your view controllers inherit:
extension UIViewController {
func background() {
var imageView = UIImageView(frame:CGRectMake(0, 0, 100, 300))
imageView.image = UIImage(named:"bg.jpg")
self.view.addSubview(imageView)
}
}
Now any view controller in your app can just say self.background() (or simply background()) to call this method.