How to access storyboard items without #IBOutlets? - swift

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!"

Related

Using NSPageController in History Mode (Content Mode) and no View Controllers?

I'm building an app with three views and I want the user to be able to swipe between them but also navigate between them by using custom buttons, like browsing in safari but only with three available views. The user can navigate between them in a somewhat random order and I provide back and forward buttons in a toolbar.
After reading Apple's documentation I believe history mode is the option I'm looking for as I can provide the views separately and navigation is not predefined like in book mode, however when I implement it it adds my view to the arrangedObjects property but the view itself doesn't change. I've correctly wired the page controller view programmatically to a subview of the app's contentView and I can see said view correctly displayed while debugging the view hierarchy.
The "Page Controller Managed View" is set as NSPageController.view programatically.
When I try using navigateForward(to:) the view gets added correctly to the arrangedObjects and the selectedIndex is correctly set but the NSPageController.view doesn't change at all (remains a blank NSView like the loaded view from storyboard). This is my code:
class MainViewController: NSViewController {
// MARK - Instance Properties
var notificationObservers: Array<NSObjectProtocol> = []
var pageController: NSPageController?
// MARK - Interface Builder Outlets
#IBOutlet var mainContentView: NSView? // Named "Page Controller Managed View" in storyboard
override func viewDidLoad() {
super.viewDidLoad()
pageController = (NSStoryboard.main!.instantiateController(
withIdentifier: "MainPageController") as! NSPageController)
pageController!.delegate = AppDelegate
pageController!.view = mainContentView!
notificationObservers.append(
NotificationCenter.default.addObserver(
forName: NSApplication.didFinishLaunchingNotification,
object: NSApp,
queue: .main,
using: { [weak self] (notification) in
// If the 'arrangedObjects' property is 0 it means no other object has set the first view
if self?.pageController!.arrangedObjects.count == 0 {
// Set the first view
self.pageController!.navigateForward(to: AppDelegate.firstView.nsView)
}
})
)
}
deinit {
// Perform cleanup of any Apple Notification Center notifications to which we may have registered
notificationObservers.forEach { (observer) in
NotificationCenter.default.removeObserver(observer)
}
}
}
This is the view hierarchy while running the app:
What am I doing wrong? Why is the page controller view not being updated with the navigateForward(to:) view?
This could shorten the amount of code I have to use by a lot compared to using view controllers (Book mode) so any help is appreciated.

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).

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.

How to have several buttons all segue to the same view controller?

I am new in iOS and storyboard and I need some advice on the possible ways of designing the architecture of the new app. I will have some data saved in the database and I will have a UIViewController page with just some buttons that have some names of some categories. When let's say 'All' is clicked or 'Business' is clicked, it will fetch data show according to that category to a UITableViewController.
Now I am confused on one part:
In story board is it allowed to create multiple segues from different buttons to the same ViewController Class? So if that is not allowed, do I have to create 6 different UITableViewController classes to make segues for 6 categories?
And if it is allowed to make segues from multiple buttons to a single UIViewController, how do I send a parameter in storyboard to make it realize that I clicked a specific button to go to the UIViewController.
Should I make a custom constructor in the UITableViewController which takes a parameter and I will initial that constructor from the other button click methods? So that it displays results in terms of category? Or is there a way storyboard does this more easily?
Sure, you can do that.
After creating all your buttons, control-drag from each button to the next view controller and create (for example) a "push" segue. Next, click on the segue graphic to select the segue. Switch to the Attributes inspector and give segue an identifier, like "All" or "Business". You can look at the segue's identifier in your -prepareForSegue: method to figure out which segue caused the transition, and hence which category to display.
I don't recommend that approach, though. There's no need to create half a dozen segues that all do exactly the same thing. I'd use an action that triggers a single segue instead:
Set the tag property of each button to a unique value so that you can differentiate between the buttons.
Add an action method to your first view controller (the one that manages the buttons) named something like -(IBAction)switchToCategoryFromButton:(id)sender.
Connect all the buttons to that single action.
Implement the action such that it looks at sender.tag to figure out which button was pressed, uses that to determine which category to load, and then triggers the one segue to the next view controller programmatically.
Having a single segue triggered by an action seems a lot neater than many segues. Code to do that might look like this:
- (IBAction)switchToCategory:(UIControl*)sender
{
// map the tapped button to a category
switch (sender.tag) {
case kAllButton: {
self.category = kAllCategories;
break;
}
case kBusinessButton: {
self.category = kBusinessCategory;
break;
}
default: {
self.category = kAllCategories;
break;
}
}
// start the segue
[self performSegueWithIdentifier:kCategorySegue sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// tell the destination view controller what category to display
if ([segue.identifier isEqualToString:kCategorySegue]) {
[(NextViewController*)segue.destinationViewController setCategory:self.category];
}
}
Use
self.performSegueWithIdentifier("yourViewSegue", sender: sender) under your event for handling button's click like :
#IBAction func redButtonClicked(sender: AnyObject) {
self.performSegueWithIdentifier("redView", sender: sender)
}
redView is the segue Identifier .
You can use same function for your button event handling & can distinguish button touch by assigning them different tags.