Swift: use variable for UITableViewController subclass - ios

Take this line of code:
let controller = storyboard.instantiateInitialViewController() as! MyCustomTableViewController
Now I want to replace MyCustomTableViewController by a variable, something like this:
var customTVC: UITableViewController
customTVC = MyCustomTableViewController
let controller = storyboard.instantiateInitialViewController() as! customTVC
The compiler complains with:
Cannot assign a value of type customTVC.Type to type UITableViewController in coercion
I get the message, but what is the way to do this properly? The obvious point is that the exact sort of subclass can vary and I want to implement that controller reference only once.

If I understand your question correctly you can use typealias:
typealias customTVC = MyCustomTableViewController
let controller = storyboard.instantiateInitialViewController() as! customTVC
Take a look at the documentation here under "Type Aliases".

Related

Accessing Data Model in UITabBarController from a Struct

I have an app that uses the UITabBarController and I have a model which I use to pass information between the various Tabs
class BaseTBController: UITabBarController {
var title: String = ""
var valueData = Double()
override func viewDidLoad() {
super.viewDidLoad()
}
Normally within any of the Tabs in the TabBars, I can just do:
let tabbar = tabBarController as! BaseTBController
let valueData = Int(tabbar.valueData) == 0 ? Int(UserDefaults.standard.double(forKey: UDKeys.valueData)) : Int(tabbar.valueData)
Now, the situation I'm in is like this, I would like to use the data from the Tabbar model data in a helper function (as struct)
Doing this, it doesn't work. Wondering if there is a way to do this and my googling/SO searching skills are just not up to par. TX
struct DataFieldOptions{
static func showValueAs() -> String {
let tabbar = tabBarController as! BaseTBController
let valueData = Int(tabbar.valueData) == 0 ? Int(UserDefaults.standard.double(forKey: UDKeys.valueData)) : Int(tabbar.valueData)
return String(valueData)
}
This seems a bit of an odd approach. If you are adding a "helper" I would expect you'd have a data-manager class rather than using a var in the tab bar controller.
However, tabBarController is an Optional property of a view controller. If you want to access it from outside the view controller you need to give it a reference.
As a side note, you're showing -> String but you're returning an Int ... I'm going to guess you're really planning on formatting the value as a string in some way, so here's how to do it returning a string:
struct DataFieldOptions{
static func showValueAs(vcRef vc: UIViewController) -> String {
// safely make sure we have access to a tabBarController
// AND that it is actually a BaseTBController
if let tbc = vc.tabBarController as? BaseTBController {
let v = Int(tbc.valueData) == 0 ? Int(UserDefaults.standard.double(forKey: UDKeys.valueData)) : Int(tbc.valueData)
return "\(v)"
}
return ""
}
}
Then, from a view controller, you would call it like this:
let str = DataFieldOptions.showValueAs(vcRef: self)

Read array from other class in Swift

I need to read an array which is in ViewController-A from ViewController-B in order to fill the data of an UIPickerView which is in ViewController-B. How could I do it ?
I have tried this answer:
https://stackoverflow.com/a/31574620/7127489
But I don't know why I can't instantiate my ViewController-B as it says in order to pass my filled array to an empty array in the other class.
Any idea ?
Thank you very much!
You can use delegates to pass data (many examples of delegation here on SO) or here
or you can do something like this:
If in navigationController:
if let navController = self.tabBarController?.viewControllers?[1] as? UINavigationController, let yourController = navController.viewControllers[0] as? yourVC{
yourArray = yourController.array
}
If not in navigationController:
if let yourController = self.tabBarController?.viewControllers?[1] as? yourVC{
yourArray = yourController.array
}
if your controller-B is seuged frome controller-A,you shuould declare a property in controller-B,and assign the value you need to controller-B when segueing.
Set up a static variable storing the array in 'ViewControllerA', then you can access it from ViewControllerB using ViewControllerA.array = [1,2,3].
You can set up the static variable as static var array = [Int]()
You would be knowing the the index of ViewControllerB in tab bar.
Declare an array in ViewControlleB. Like
var pickerData = [String]() // use appropriate type depending upon need.
Whenever the data in ViewControllerA get update update the data in ViewControllerB like this
let vcb = self.tabBarController?.viewControllers?[index of ViewControllerB] as? ViewControllerB
vcb?.pickerData = newData

Passing in a type as an argument in swift

I'm making a method that animates a transition from one view controller to another in Swift.
The only thing that varies from method call to method call is the particular subclass of UIViewController.
Therefore, I'm assuming the only things I need to pass in for the method to work the way I want it to is not the instance of a subclass of UIViewController but simply the subclass type itself. How would I write this in Swift?
func doAnimation(fromType, toType) { //don't pass in instances, but types to be instantiated (this is the part that I need help with)
... //declare fromVC and toVC
let topVC = !self.isPresenting ? fromVC as fromType : toVC as toType //assert what type top view controller will be be
let bottomVC = !self.isPresenting ? fromVC as! fromType : toVC as toType //assert what type bottom view controller will be
...
}
Thank you in advance.
import Foundation
class C {}
func f<T>(type: T) {
switch type {
case is Int.Type:
print("Integer type")
case is String.Type:
print("String type")
case is C.Type:
print("C type")
default:
print("something else ... :-)")
}
}
f(Int.self) // Integer type
f(C.self) // C type
f(String.self) // String type
f(Any.self) // something else ... :-)

Implementing a UISearchController in Swift Core Data Project

I'm presently taking an iOS development course. As part of an assignment, I'm tasked with creating a UISearchController in a note tracking project using Core Data in Swift.
Every example I've found is in Objective-C or is filtering a static array. Apple's "sample" code, updated in December 2014 doesn't compile in Xcode 6.3.
To add a UISearchController, I've got 3 primary tasks to do:
1) Create a view controller to present search results. I'm using a TableViewController.
2) Create a UISearchController, and pass it my search results view controller.
What's "stumping" me is now to get a hold of the objects in the managedObjectsContext. Prior to attempting to add a UISearchController, my app works fine. I can add, edit, and delete items. I'm using the "canned" Core Data code in Xcode 6.3 with the stack in AppDelegate.
class MasterViewController: UITableViewController, NSFetchedResultsControllerDelegate
var searchController: UISearchController? = nil
func addSearchBar() {
var resultsController = SearchResultsTableViewController()
resultsController.notes = // stumped what to call the notes. Trying to call an array from the Notes class below
resultsController.delegate = self
searchController = UISearchController(searchResultsController: resultsController)
searchController!.searchResultsUpdater = resultsController
searchController!.searchBar.frame = CGRect(
x: searchController!.searchBar.frame.origin.x,
y: searchController!.searchBar.frame.origin.y, width: searchController!.searchBar.frame.size.width, height: 44.0)
tableView.tableHeaderView = searchController!.searchBar
self.definesPresentationContext = true
}
3) The UISearchController will notify its searchResultsUpdater (a class that conforms to UISearchResultsUpdating) when the search text changes. I'm using my search results view controller implement this protocol so I can update the filtered results.
Below is my Note: NSManagedObject class:
import Foundation
import CoreData
class Note: NSManagedObject {
#NSManaged var dateCreated: NSDate
#NSManaged var dateEdited: NSDate
#NSManaged var noteTitle: String
#NSManaged var noteBody: String
// TODO: adding this to make it easier to handle names
class func notes() -> NSArray {
let whereMyNotesAreStored = // Need syntax for where my MyManagedObjectContext is located
let dataArray = NSArray(contentsOfFile: whereMyNotesAreStored!)
var notesArray = NSMutableArray()
for dictionary in dataArray {
var note = Note()
note.noteTitle = dictionary["noteTitle"] as! String
note.noteBody = dictionary["noteBody"] as! String
note.dateCreated = dictionary["dateCreated"] as! String
note.dateEdited = dictionary["dateEdited"] as! String
notesArray.addObject(note)
}
return NSArray(array: notesArray as! [AnyObject])
}
}
There are two approaches to setting the context:
Calling back to the App Delegate:, like this
let appDelegate : AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context = appDelegate.managedObjectContext!
or passing the context forward from the App Delegate to the Master View Controller, which then passes it on to any subsequent view controllers, etc. Each view controller will need a property defined for the context; when a new VC is instantiated, the context is set before the VC is presented/pushed, eg:
class CustomViewController : UIViewController {
var managedObjectContext : NSManagedObjectContext
...
and, when loading a new view controller,
let newVC = CustomViewController()
newVC.managedObjectContext = self.managedObjectContext
...
To access the objects, use either a NSFetchRequest or NSFetchedResultsController to create an array of results, which you can then pass to the resultsController. eg. for a fetch request:
let fetch = NSFetchRequest(entityName: "Notes")
var error : NSError? = nil
let fetchedResults = managedObjectContext?.executeFetchRequest(fetch, error: &error)
(Your notes() function is on the wrong track - you would not use NSArray(contentsOfFile:) to access CoreData objects. Also, you must use the designated initialiser for NSManagedObject subclasses: so not var note = Notes() but var note = Notes(entity: NSEntityDescription, insertIntoManagedObjectContext: NSManagedObjectContext?)

Passing an object from one viewcontroller to another viewcontroller got nil in swift

Recently, I'm learning about CoreData in Swift. My purpose is about to send the value of one object "editContact" in class "AllContactTableViewController" as code below.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let managedObjectContext:NSManagedObjectContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Contact", inManagedObjectContext: managedObjectContext)
var request = NSFetchRequest(entityName: "Contact")
request.returnsObjectsAsFaults = false
var results: NSArray = managedObjectContext.executeFetchRequest(request, error: nil)!
let editContactView : EditContactTableViewController = EditContactTableViewController()
var editContact : Contact = results.objectAtIndex(indexPath.row) as Contact
editContactView.editContact = editContact
println("\(editContactView.editContact)")
}
to another viewcontroller called "EditContactTableViewController" (as code below)
class EditContactTableViewController: UITableViewController {
var editContact : Contact!
override func viewDidLoad() {
super.viewDidLoad()
firstNameField.text = self.editContact.firstName
lastNameField.text = self.editContact.lastName
phoneField.text = self.editContact.phone
emailField.text = self.editContact.email
companyField.text = self.editContact.company
addressField.text = self.editContact.address
}
}
then it caused the error as "fatal error: unexpectedly found nil while unwrapping an Optional value"
in the log. It seems the value of object called "editContact" in this class has changed to nil.
Do you have idea how to fix this problem?
First, use a NSFetchedResultsController. There are many advantages, and you also will safely get the object you need with
self.fetchedResultsController.objectAtIndexPath(indexPath)
If your contact is displayed in the cell you must already have fetched it. So it does not make any sense to fetch it again. Also you have an unused variable (entity) in your very verbose but unnecessary code.
The best way is to use a segue in storyboard from the cell to the edit controller. You can then set up the object in prepareForSegue.
One nice setup is to subclass the table view cell and give it a property of type Contact. The displaying of the attributes in the cell can be handled by the subclass, helping you to uncluttered the table view controller. You can then easily retrieve the object in prepareForSegue from the sender parameter.

Resources