Xcode storyboard compilation issue for version 6.3 - xcode

I am working on a simple table with custom cell in a View controller (iPad App). I have created a separate popover view controller to add new record to the table (data entry only) and I am trying to use same view controller to edit the row also (the selected row in the table view). The popover view works well for creating the new record.
But when I created a connection from the table view cell to the same view controller, the compilation fails with following message:
Interface Builder Storyboard Compilation Error : Couldn't compile
connection: IBCocoaTouchOutletConnection: => anchorView =>
IBUITableViewCell: 0x7fad4ca76d70
If I make the connection as PUSH or MODAL, the compilation goes through and I can execute the app.
I wanted to have the Add/Edit record view as a popover as this has only 4 fields. Now it works only if it is push mode. Can you please help me in solving this issue?

I was able to get this resolved using the custom segue from the cell to the view controller (popover mode). Following are the steps:
Created the custom segue in Storyboard from custom cell to target view controller.
Created a new class file subclass of UIStoryboardSegue and linked this to the custom segue in storyboard.
Used the segue id in prepareForSegue (as usual).
Below is the code I used in the custom segue class:
class myCellSegue: UIStoryboardSegue {
override func perform() {
var sourceVC: UIViewController = self.sourceViewController as! UIViewController
var destVC: UIViewController = self.destinationViewController as! UIViewController
destVC.modalPresentationStyle = .Popover
destVC.preferredContentSize = CGSizeMake(600, 500)
let popoverPresentationVC = destVC.popoverPresentationController
popoverPresentationVC?.sourceView = destVC.view
popoverPresentationVC?.permittedArrowDirections = .Down
// ==== View parameters ========
destVC.view.backgroundColor = UIColor.purpleColor()
sourceVC.view.addSubview(destVC.view)
sourceVC.presentViewController(destVC, animated: false, completion: nil)
}
}
BTW - we can use one custom segue for multiple segue connections.

Related

Xcode - How can i programmatically embed/change view controller within a container view?

I am trying to programmatically set / or change the embedded View Controller inside a Controller View.
I have 3 different View Controllers, that I would like to show in the Container View, all depending on if the user is logged in or not.
I have looked around and tried a bunch of code, I found one that worked, but the code changed the self view, and not the view containers view.
A lot of the code I have tried have also not been in Swift 3, so as a new app developer, this has been quite stressful, as I tried to convert it to Swift 3.
Can anyone provide a solution for changing the embedded view controller inside a view container? Thanks.
I might have found a solution for this. I'm answering here, in case it can help anyone else in my situation.
What I did was add a new View Controller and then embed it to the View Container - This will work as a "master view" - I then use this blank view controller to decide which other view controller should be changed within the self of the blank.
Here's some code I have in the blank view controller, but I suppose the blank view controller can also be used as a master view controller (in my case for "Account"), and then it can add the login/register view as a child.
override func viewDidLoad() {
super.viewDidLoad()
updateView()
}
private lazy var loginViewController: loginViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as! loginViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private lazy var registerViewController: registerViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "registerViewController") as! registerViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChildViewController(viewController)
// Add Child View as Subview
view.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = view.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
private func updateView() {
add(asChildViewController: registerViewController)
/*
if segmentedControl.selectedSegmentIndex == 0 {
remove(asChildViewController: sessionsViewController)
add(asChildViewController: summaryViewController)
} else {
remove(asChildViewController: summaryViewController)
add(asChildViewController: sessionsViewController)
}
*/
}
Credit to this guy: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/
The easiest way to do that is to have 3 container views in Storyboard above each other, each one of them is connected to a different view controller, then in your code, make 2 of these 3 container views hidden and leave one visible based on your business needs.
This is described in detail in this blog post. Hope this helps!

Global variable and optional binding in Swift

I have some quite simple doubt regarding optional binding,global variable & wrapping and unwrapping . Since I am new to SWIFT, its very important to understand the tits & bits of their concepts.
1) In Swift if I declare a global variable, I have 2 options either to make it optional or non optional, so let I am having 2-4 or more optional variables . So is it advisable to optional bind all those variables in
viewDidLoad() method// so that I could use them without any problem of unwrapping and fatal error in my program.
2) Let me make myself more clear by the following example- I have 2 VC in my project VC1 & VC2 . VC2 has a text field in which user enters some value and displays it in a tabelview in VC1.
In Vc1
var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2.
As you can see, my VC1 is the first view controller that loads when my project runs and I am using an optional variable to populate my tabke vuew that is
'arr'
So when the app runs for the first time its empty . So it might cause a fatal error while using its value in the code. So what is its solution whether to unbind it in the
viewDidLoad()
method or in all total declare an empty NSMutable array type in place of optional type .
Thanks in advance.
I'll start by repeating the my comment from above.
Possibly you've misunderstanding the concept of global variables in Swift.
If you have a global variable, you won't have to "pass" it between any views/methods/classes etc, because the variable is defined at global scope (accessible everywhere).
Generally global variables is not a good idea, and something that you want to avoid.
Regarding the matter of global variables and swift, you really should include singletons into the discussion. See e.g. the following existing SO thread(s):
Any reason not use use a singleton "variable" in Swift?
(How to create a global variable?)
(Declaring Global Variables in Swift)
Communication between TableViewController and ViewController by means of segues (prepare for & unwind segues)
(This answer ended up being very and probably a bit too thorough, as I didn't know in detail what your current tableview/viewcontroller program state looks like. Sorry for the lengthy answer and any inconvenience it might bring to readers of it).
Now, lets leave global variables and discuss one (among other) viable options for the communication between the two controllers in your example. From your question, I'll summarize your example as follows
VC1: storyboard entry point, a UITableViewController consisting of UITableViewCells, where, in these cells, you display some text, say, via instances of UILabel.
VC2: a UIViewController, accessible from the cells of VC1, containing an UITextField instance. When user enters text into this text field, your want the text to be displayed in the associated cell in VC2 (associated in the sense that it was the cell in VC1 that was used to access VC2).
We'll associate VC1 and VC2 with (cocoa touch) classes TableViewController (TableViewController.swift) and ViewController (ViewController.swift), respectively. The cells in the table view controller will be associated with (cocoa touch) class TableViewCell (TableViewCell.swift). Details for these classes follow below.
For this simple example, note that we will not embed VC1 into a navigation controller (which is otherwise appropriate for table view -> view navigation).
We'll start in the storyboard, adding objects (drag-and-drop from object library) for a Table View Controller and a View Controller. The table view container will also, automatically, contain, in its Table View, a TableViewCell. Continuing in the storyboard:
Add a UILabel object to the TableViewCell container in the Table View Controller (align it as you wish)
In the View Controller, add a Text Field object and a Button object (align them as you wish).
Set the entry point to the Table View Controller.
Thereafter Ctrl-drag a 'Show' segue from the TableViewCell to the View Controller.
Select the Show segue and, from the Attributes inspector, enter an identifier for it, say, ShowDetail.
Finally, with the TableViewCell selected, (as above; from the attribute inspector), enter an identifier for the cell. Here, we'll use simply use identifier TableViewCell.
We now leave the storyboard for now and implement three classes, associated with the Table View Controller, the View Controller and the formers' TableViewCell.
We start with the Table View Controller, and implement our UITableViewController sub-class. Note that here, instead of using an NSMutableArray to hold the texts of the UITextLabel in each cell, we'll simply use a String array.
// TableViewController.swift
Import UIKit
class TableViewController: UITableViewController {
// Properties
var userTextLabels = [String]()
var numberOfCells: Int?
override func viewDidLoad() {
super.viewDidLoad()
numberOfCells = loadSampleTextLabels() // Load sample labels.
}
func loadSampleTextLabels() -> Int {
userTextLabels += ["Label #1", "Label #2", "Label #3"]
return userTextLabels.count
}
// func numberOfSectionsInTableView(tableView: UITableView) ...
// func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = ("TableViewCell")
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell
// Text for current cell
let cellText = userTextLabels[indexPath.row]
cell.userSuppliedTextLabel.text = cellText
return cell
}
// ... communication?
}
Where the two commented out methods are standard methods used in any UITableViewController, for number of sections (e.g. return 1) and cells (e.g. return (numberOfCells ?? 0)) in the table, respectively. I'll leave fixing these to you.
Now, we associate the TableViewCell object(s) in the table view with instances of a subclass to UITableViewCell. Here, we'll use a very simple class for our cells; each cell just containing a single UILabel instance (created via storyboard Ctrl-drag as an #IBOutlet from the UILabel in the table view cells).
// TableViewCell.swift
import UIKit
class TableViewCell: UITableViewCell {
// Properties
#IBOutlet weak var userSuppliedTextLabel: UILabel!
// Ctrl-drag from UILabel (in TableViewCell) in storyboard
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Finally, for the view controller that is accessed from the table view cells: use a single #IBOutlet to the UITextField used for user text input, and handle events in this text field using the pre-existing UITextFieldDelegate. E.g.:
// ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
// Properties
#IBOutlet weak var userSuppliedText: UITextField!
// Ctrl-drag from storyboard...
var cellText: String?
override func viewDidLoad() {
super.viewDidLoad()
userSuppliedText.text = cellText ?? "..."
// Handle the user input in the text field through delegate callbacks
userSuppliedText.delegate = self
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
// User finished typing (hit return): hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
cellText = textField.text
}
}
We've also declared a string property (cellText) here, that will as act as container for communication between VC1 and VC2.
We return to the storyboard and---from the Identity inspector---associate the three storyboard objects (Table View Controller, View Controller, TableViewCell) with their associated classes that we've just written above.
We're now almost at our goal; it only remains to specify how to communicate between the two controllers.
We'll begin with communication from VC1 to VC2. In your comment above, you were on the right track (for this specific solution, anyway) by looking at the prepareForSegue(...) method. In the class for the Table View Controller, we add the following method:
// ... add to TableViewController.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "ShowDetail" {
let viewController = segue.destinationViewController as! ViewController
if let selectedCell = sender as? TableViewCell {
let indexPath = tableView.indexPathForCell(selectedCell)!
let currentTextInCell = userTextLabels[indexPath.row]
viewController.cellText = currentTextInCell // <-- note this
}
}
}
Hence, for VC1->VC2 communication, we can (in this example) bring whatever existing text that is currently occupying the UILabel in the sender cell (as is specified by the String array userTextLabels). Look at the viewDidLoad(...) method in the ViewController.swift to see how this value is passed from VC1 and set as default text in the UITextField in VC2.
Now, for communication VC2->VC1, which was the specific communication direction you were asking about, add another method (programmatically), again to TableViewController.swift:
// ... add to TableViewController.swift
#IBAction func unwindToTableView(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? ViewController,
text = sourceViewController.cellText {
// ^ note 2nd clause of if let statement above
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update cell text
userTextLabels[selectedIndexPath.row] = text
tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
}
}
}
Here, we define an unwind action that, when triggered, retrieves the cellText property of the view controller that was the source of the segue, i.e., in our case, the instance of ViewController. But how do we trigger this action?
Return to the storyboard and the View Controller. Note the three little icons in the top of the View Controller object, more specifically, the right-most of these, named Exit. Ctrl-drag an action from your Button to the Exit icon, and select the unwindToTableView Action Segue. When you click your button the view controller, the view unwind (exit) and land at the unwindToTableView method in the TableViewController.
The resulting app should look something like this:
This was way longer than I had expected, but once you get started writing... Anyway, the method above uses, naturally, no global variables, but make use of references to future (prepareForSegue) or historic (unwindToTableView) views to get (generally from current or historic view) or set (generally in current of future view) values by using these references (to future/historic view).
Apple has their own very thorough tutorial on an example app in the tableviewcontroller/viewcontroller context that I would recommend going over. I found it very valuable myself when I started coding Swift.
Start Developing iOS Apps (Swift)

How to create modal slide-out window in Mac OS?

How can I create modal slide-out window/view "in-window" in Xcode like in these screenshot?
I've tried create new Window controller with "Authentication panel style" animation but then I'm receiving only Xcode crashes.
That kind of modal window is called a Sheet. It's very easy to get this behavior with a Storyboard segue, or programmatically with an NSViewController subclass. The example below is just a blank OS X Cocoa application as created by Xcode. (I chose Swift as the language, but it will work the same way with Objective-C.)
The only things I added to the storyboard was a second View Controller for the sheet view, and a label and pushbutton on each view.
Displaying The Sheet View With A Storyboard Segue
With the Sheet View controller selected and the Connections Inspector tab displayed, connect "Presenting Segues - sheet" to the "Display Sheet" button.
Connect "Received Actions - dismissController:" to the "Close Sheet" button.
That's it! There's no code needed to make this example work; just build and run.
Displaying The Sheet View Programmatically
Note that Xcode creates the default project with two custom class files. In the Storyboard, AppDelegate.swift is represented in the Application scene:
We don't need to use the AppDelegate for this example, but you could use it for interaction with the Main Menu, or other things.
The custom ViewController.swift custom class will be used to present the sheet. It is represented in the View Controller scene:
To instantiate the Sheet View Controller programmatically, it needs a Storyboard ID. Here, we'll give it the ID "SheetViewController". Note that it's still a plain NSViewController; we don't need to make it a custom class for this example, but your application might want to:
Displaying the ViewController.swift file in the assistant editor, Ctrl-drag a connection from the "Display Sheet" button into the custom class. This will create stub code for an #IBAction function we'll name "displaySheet":
In the ViewController.swift file, we'll implement the Sheet View Controller as a lazy var. It will get instantiated only once, the first time it's accessed. That will happen the first time the displaySheet function is called.
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateControllerWithIdentifier("SheetViewController")
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
Swift 4 version:
// ViewController.swift
import Cocoa
class ViewController: NSViewController {
lazy var sheetViewController: NSViewController = {
return self.storyboard!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "SheetViewController"))
as! NSViewController
}()
#IBAction func displaySheet(sender: AnyObject) {
self.presentViewControllerAsSheet(sheetViewController)
}
}
As in the first example, the "Close Sheet" button is connected to the "dismissController:" action on the Sheet View Controller. Alternatively, you could call that function programmatically from your ViewController class:
self.dismissController(sheetViewController)
For more information, refer to the Apple "Sheets Programming Topics" document:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Sheets/Sheets.html
Objective-C version:
- (IBAction)displaySheet:(id)sender {
NSStoryboard *storyboard = [NSStoryboard storyboardWithName:#"Main" bundle: nil];
NSViewController * vc = [storyboard instantiateControllerWithIdentifier:#"SheetViewController"];
[self presentViewControllerAsSheet:vc];
}

Presenting a navigation controller on a View Controller from storyboard

In my app i have a storyboard which has initial view controller as X and then i have a navigation controller which has couple of view controllers attached with it through segue but there is no connection between ViewController x and this navigation controller.
Now based on a button tap in View controller x i want to present this navigation controller.
I am doing this :
func showDashBoard()
{
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let next = storyBoard.instantiateViewControllerWithIdentifier("navigation") as! EBNavigationController
self.presentViewController(next, animated: true, completion: nil)
}
So the navigation view is getting presented with the root view controller but its not showing me animation and also i am getting a warning "Attempt to present on whose view is not in the window hierarchy!
"
Can some one please help me in figuring out what is the best way to do this.
Thanks,
The animation problem can be fixed using next.modalPresentationStyle and next.modalTransitionStyle. Lookup the enums for the possible values and give it the one you want.
You usually get the "Attempt to present on whose view is not in the window hierarchy!" errors when you are calling this function before the view is fully loaded. For instance if you call it in the viewDidLoad method. You should call it only after viewDidAppear is called.

Split View Controller: How to connect Master View Controller to Detail View Controller?

(Xcode6-beta3, Swift, iOS8, iPad)
In an iPad split-view controller, how do I link the Master View Controller to the Detail View Controller?
In other words, when the user taps on an item on the left, how do I change the view on the right?
I know that in didSelectRowAtIndexPath, I need to call a method... but how do I call a method in the Detail View Controller from the Master View Controller?
Example
Imagine an app to display information on different types of cheeses. We begin by dragging a split-view controller onto the storyboard. A table of items in the master view on the left is set up to read as follows.
Swiss
Cheddar
Brie
On the right, there is simply a Web View inside of the detail view controller, named cheeseViewController. Therein, HTML documents about the selected cheese will be displayed.
An IBOutlet is wired from the web view into cheeseViewController, and a method named 'changeCheese' is set up in the Detail View Controller delegate to swap out the document.
How can I make a tap on "Cheddar" change the information in the detail view?
EDIT: Do I have to modify my AppDelegate.swift file? Using a Master-Detail template, I tried the following, with no luck:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
splitViewController.delegate = navigationController.topViewController as Paragraph
return true
}
I hope I understood your problem correctly: You would like to show the detail information of a selected cheese in your Detailview.
When you create a new Master-Detail-View application in XCode 6 Beta 3, there will be a variable called "detailItem" in your DetailViewController.Swift file:
var detailItem: AnyObject? {
didSet{
self.configureView()
}
You set this detailItem in your MasterViewController.Swift file in the following function:
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?){
if segue.identifier == "yourSegueIdentifier" {
let indexPath = self.tableView.indexPathForSelectedRow()
let cheeese = yourCheeseArrayWithDetailInformation[indexPath.row]
(segue.destinationViewController as DetailViewController).detailItem = cheeese
}
}
(Assuming, that you have linked the views with a segue with the identifier: "yourSegueIdentifier" and an array of detailinfo called "yourCheeseArrayWithDetailInformation")
The above mentioned function "configureView" in the DetailView can now access your detailItem, which contains the contents of "cheeese"
I hope this helps you.
Why don't you just post a Notification from didSelectRowAtIndexPath in your Master and add an observer in your Detail View most likely inside your viewDidLoad. You also can handle the selector within the observer method with closure.
If you didn't create a master-detail app (so you have no detailItem), you might use this:
if let
mySplitViewController = splitViewController,
detailView = mySplitViewController.childViewControllers.last as? DetailViewController {
// do something with it
}

Resources