How can I call this function? - class

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.

Related

Prevent user interaction with SKScene under UIViewController

I have a SKScene that acts as somewhat of a main menu. And I use container views that have various view controllers such as settings etc...
The problem is, players can still tap on nodes etc behind the view controller in the container view. Is there a way to prevent this?
Had help from a friend who wrote an extension that solved this, presented the view over the top:
extension UIViewController {
var topPresentedViewController: UIViewController {
if let vc = self.presentedViewController {
return vc.topPresentedViewController
}
return self
}
}

Accessing UINavigationController from rootVC Subview (subview loaded from Nib)

The main ViewController is embedded in a UINavigationController subclass, and the VC has a subview that is loaded from a nib. The subview is called MenuView, and contains UIButtons that will link to other VCs.
To keep my main ViewController less unruly, I have put all these buttons into a subview that loads from a nib that animates the menu opening and closing.
However, I would like to present other view controllers from these, sometimes "Modally", sometimes "Show". What I have done seems to work, but I just want to know if this is alright, or if I have caused some unwanted effects that I'm unaware of (like a strong reference cycle that would cause a memory leak, or something). Or is there a better way to do this?
Some code:
In MenuView.swift
class MenuView: UIView {
var navigationController = CustomNavigationController()
func combinedInit(){
NSBundle.mainBundle().loadNibNamed("MenuViewXib", owner: self, options: nil)
addSubview(mainView)
mainView.frame = self.bounds
}
#IBAction func optionsAction(sender: AnyObject) {
self.navigationController.performSegueWithIdentifier("presentOptions", sender: self)
}
In ViewController.swift
menuView.navigationController = self.navigationController as! CustomNavigationController
Short answer: No, it is not alright to access a view controller from within some view in the hierarchy, because that would break all the MVC rules written.
UIView objects are meant to display UI components in the screen and are responsible for drawing and laying out their child views correctly. That's all there is. Nothing more, nothing less.
You should handle those kind of interactions between views and controllers always in the controller in which the view in question actually belong. If you need to send messages from a view to its view controller, you can make use of either the delegate approach or NSNotificationCenter class.
If I were in your shoes, I would use a delegate when view needs some information from its view controller. It is more understandable than using notification center as it makes it much easier to keep track of what's going on between. If the view controller needs some information from a view (in other words, the other way around), I'd go with the notification center.
protocol MenuViewDelegate: class {
func menuViewDidClick(menuView: MenuView)
}
class MenuView: UIView {
var weak delegate: MenuViewDelegate?
#IBAction func optionsAction(sender: AnyObject) {
delegate?.menuViewDidClick(self)
}
}
Let's look at what's going on at the view controller side:
class MenuViewController: UIViewController, MenuViewDelegate {
override func viewDidLoad() {
...
self.menuView.delegate = self
}
func menuViewDidClick(menuView: MenuView) {
navigationController?.performSegueWithIdentifier("presentOptions", sender: self)
}
}
For more information about communication patterns in iOS, you might want to take a look at this great article in order to comprehend how they work.

performSegueWithIdentifier from a NSView subclass?

I have a document window that contains a number of NSView subclasses, switched between using a tab control. Each of the subclasses, and the window's ViewController, support different user actions accessed through menu items tied to the First Responder.
I'd like to perform a segue from one of those views in response to a menu item. However, NSView does not support performSegueWithIdentifier, it appears to be something that is part of NSViewController alone.
Can someone suggest a way around this? I have seen suggestions to pass the VC into the views, but I am not clear how to do that. Or perhaps there is a better way?
view.containingController.performSegue()
note: you have to add containingController to your views
I WOULD add the viewController to the responder chain and then make containingController a computed property in an extension!
e.g. add vc as responder:
override func awakeFromNib() {
super.awakeFromNib()
self.nextResponder = self.view
for subview in self.view.subviews {
subview.nextResponder = self
}
}
e.g. containingController in extension
extension NSView {
var containingController: NSViewController? {
get {
while(self.nextResponder != nil) {
if(self.nextResponder is NSViewController) {
return self.nextResponder
}
}
return nil
}
}
}
You could do that (see Daij-Djan's answer), however it is not what I would recommend, since a hypothetical programmer who will be using your code, but is not familiar with it (let's say, you in a year :) ) might be caught by surprise by such behaviour.
I would recommend you to add a delegate (conforming to your custom protocol, let's call it MyViewDelegate) to your NSView with a method like viewRequiresToPerformTransition(view: YourViewSubclass). Then you implement this method (more generally, you conform to MyViewDelegate protocol) in your view controller and inside its implementation perform any segue you want.

Segue stops working after first segue

I am trying to use a custom segue, that makes the view 'scroll' to the right or left, when a button is clicked. I added a custom class that looks like this
class horizontalSegue : UIStoryboardSegue {
override func perform() {
var oldView = self.sourceViewController.view as UIView
var newView = self.destinationViewController.view as UIView
oldView.window?.insertSubview(newView, aboveSubview: oldView)
newView.center.x = oldView.center.x + oldView.frame.width
newView.center.y = oldView.center.y
UIView.animateWithDuration(0.6, animations: { newView.center = oldView.center }, completion: { finished in Void })
}
}
but the problem is that after I segue once, I cannot segue back to the view I segued from. I think its because of the way I have the oldView and newView declared, the app isn't updating which view is which, so its segueing back to the current view when I try to segue to the first view.
If I am correct, how would I make sure the app updates which view is which?
Apple documentation says: "Regardless of how you perform the animation, at the end of it, you are responsible for installing the destination view controller (and its views) in the right place so that it can handle events. For example, if you were to implement a custom modal transition, you might perform your animations using snapshot images and then at the end call the presentModalViewController:animated: method (with animations disabled) to set up the appropriate modal relationship between the source and destination view controllers."
May be you should add something like
[self.navigationController pushViewController:vc animated:NO]
into your animation's completion handler?

Changing label in other view in 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.