So I am attempting to make a tableview out of an array of cloudkit downloaded items. However, I'm having this simple little "definition conflicts with pervious value" error. This error occurs for the cellForRowAtIndexPath function - which supposedly conflicts with the numberOfRowsInSection function. I have attempted moving/deleting brackets of the latter function and by placing a question mark/optional at the end... "-> UITableViewCell?" to no avail. Where could the error be coming from?
Also, as it stands, xcode deleted the override portion of each tableview function. Why does it sometimes stay and when is it deleted?
import UIKit
import CloudKit
class DiningTable: UITableViewController {
var categories: Array<CKRecord> = []
override func viewDidLoad() {
super.viewDidLoad()
func getRecords()
{
categories = []
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "DiningTypes", predicate: predicate)
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["Name", "Address", "Picture"]
queryOperation.qualityOfService = .UserInteractive
queryOperation.recordFetchedBlock = { (record:CKRecord) -> Void in
let categoryRecord = record
self.categories.append(categoryRecord)
}
queryOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error: NSError?) -> Void in
if (error != nil) {
print("Failed to get data from iCloud - \(error!.localizedDescription)")
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}
publicDatabase.addOperation(queryOperation
}
func tableView(tableView: UITableView, numberOfRowsInSection: Int) -> Int {
return self.categories.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("dining") as! DiningTableCell
let restaurant: CKRecord = categories[indexPath.row]
cell.RestaurantName?.text = restaurant.valueForKey("Name") as? String
let img = restaurant.objectForKey("Picture") as! CKAsset
cell.RestaurantPhoto.image = UIImage(contentsOfFile: img.fileURL.path!)
return cell
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue1" {
if let destViewController = segue.destinationViewController as? RestaurantTable {
let indexPath = self.tableView.indexPathForSelectedRow!
destViewController.indexpath1 = indexPath
}
}
}
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
Your entire class definition is nested inside your
override func viewDidLoad() {
function. Put a closing brace in after the call to super.viewDidLoad()
Are you sure that's your code or could you have accidentally removed some lines?
Because, as it stands, your table view functions are actually embedded in your viewDidLoad function... They need to be object level functions.
Note the indentation level of your code when you have Xcode indent the code for you (Right-click on the screen and select Structure->Re-indent.
import UIKit
import CloudKit
class DiningTable: UITableViewController {
var categories: Array<CKRecord> = []
override func viewDidLoad() {
super.viewDidLoad()
} // <--- you are missing this close brace here.
func getRecords()
{
categories = []
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "DiningTypes", predicate: predicate)
let queryOperation = CKQueryOperation(query: query)
queryOperation.desiredKeys = ["Name", "Address", "Picture"]
queryOperation.qualityOfService = .UserInteractive
queryOperation.recordFetchedBlock = { (record:CKRecord) -> Void in
let categoryRecord = record
self.categories.append(categoryRecord)
}
queryOperation.queryCompletionBlock = { (cursor:CKQueryCursor?, error: NSError?) -> Void in
if (error != nil) {
print("Failed to get data from iCloud - \(error!.localizedDescription)")
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}
publicDatabase.addOperation(queryOperation)
}
override func tableView(tableView: UITableView, numberOfRowsInSection: Int) -> Int {
return self.categories.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("dining") as! DiningTableCell
let restaurant: CKRecord = categories[indexPath.row]
cell.RestaurantName?.text = restaurant.valueForKey("Name") as? String
let img = restaurant.objectForKey("Picture") as! CKAsset
cell.RestaurantPhoto.image = UIImage(contentsOfFile: img.fileURL.path!)
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue1" {
if let destViewController = segue.destinationViewController as? RestaurantTable {
let indexPath = self.tableView.indexPathForSelectedRow!
destViewController.indexpath1 = indexPath
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Related
I followed this tutorial
https://www.youtube.com/watch?v=35mKM4IkHS8&lc=UgztyK4XjUuAOrKk0XJ4AaABAg.9LtwRc_M0Gv9Nt8GIlAzDo
Basically I made a NotePad App that has a core data save function.
I made this app on another view controller
So There is MainViewController > NoteViewViewController
The first time I click the notepad section it loads core data perfectly well, but if I close out the NoteView and reopen it -- it duplicates all the saved Notes in Core Data
Here is the. Note ViewController
import UIKit
import CoreData
var noteList = [Note]()
class NoteTableView: UITableViewController
{
func nonDeletedNotes() -> [Note]
{
var noDeleteNoteList = [Note]()
for note in noteList
{
if(note.deletedDate == nil)
{
noDeleteNoteList.append(note)
}
}
return noDeleteNoteList
}
var firstLoad = true
override func viewDidLoad() {
if(firstLoad == true)
{
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell
{
let noteCell = tableView.dequeueReusableCell(withIdentifier: "noteCellID", for: indexPath) as! NoteCell
let thisNote: Note!
thisNote = nonDeletedNotes()[indexPath.row]
noteCell.titleLabel.text = thisNote.title
noteCell.descLabel.text = thisNote.desc1
return noteCell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return nonDeletedNotes().count
}
override func viewDidAppear(_ animated: Bool) {
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.performSegue(withIdentifier: "editNote", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if(segue.identifier == "editNote")
{
let indexPath = tableView.indexPathForSelectedRow!
let noteDetail = segue.destination as? FocusWheelViewController
let selectedNote : Note!
selectedNote = nonDeletedNotes()[indexPath.row]
noteDetail!.selectedNote = selectedNote
tableView.deselectRow(at: indexPath, animated: true)
}
}
}
I'm sure there is a common solution but I'm not sure what it is and wasn't able to follow the posts asking similar questions as my code was different and I truthfully don't understand the mechanics well enough to apply other answers to this
I found the easiest solution was to just add these two lines so the table view refreshed every-time, then loaded the data
noteList.removeAll()
tableView.reloadData()
So the code looks something like this:
var firstLoad = true
override func viewDidLoad() {
if(firstLoad == true)
{
noteList.removeAll() //NEWCODE
tableView.reloadData() //NEWCODE
firstLoad = false
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
}
}
catch
{
print("Fetch Failed")
}
}
}
The problem is var firstLoad = true. Because every time the controller start, firtLoad always true and the app will get data from Coredata and append to noteList.
The solution is UserDefaults. The first time when you run app, firstLoad always true. So you need to save the value bool of firstLoad to UserDefaults
// Set
UserDefaults.standard.setValue(true, forKey: "firstLoad")
// Get
UserDefaults.standard.bool(forKey: "firstLoad")
import UIKit
import CoreData
class NoteTableView: UITableViewController{
var noteList = [Note]()
func nonDeletedNotes() -> [Note]{
var noDeleteNoteList = [Note]()
for note in noteList {
if(note.deletedDate == nil) {
noDeleteNoteList.append(note)
}
}
return noDeleteNoteList
}
override func viewDidLoad() {
if noteList.count == 0 {
if(UserDefaults.standard.bool(forKey: "firstLoad") == true){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Note")
do {
let results:NSArray = try context.fetch(request) as NSArray
for result in results
{
let note = result as! Note
noteList.append(note)
UserDefaults.standard.setValue(false, forKey: "firstLoad")
}
}
catch {
print("Fetch Failed")
}
}
} {
else {
UserDefaults.standard.setValue(false, forKey: "firstLoad")
}
}
}
}
And maybe you need to check duplicate value when get data from CoreData.
The app crashes with the following error when the search bar is tapped:
Not able to understand why?
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier ContactCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
This is my code:
import UIKit
class ContactViewController: UITableViewController, UISearchResultsUpdating {
var dataSource: [Contact] = []
var filteredResult = [Contact]()
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
downloadJSONUrl()
}
func downloadJSONUrl() {
let urlString = "https://us-central1-practo-contacts-sample.cloudfunctions.net/get"
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response , error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
if let NameArray = jsonObj?.value(forKey: "contacts") as? [[String: Any]] {
for names in NameArray {
var cont = Contact()
if let name = names["name"] as? String {
cont.name = name
}
if let ph = names["number"] as? String {
cont.phoneNumber = ph
}
self.dataSource.append(cont)
}
self.dataSource.sort {$0.name.lowercased() < $1.name.lowercased()}
}
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
}).resume()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as UITableViewCell
let contact = self.dataSource[indexPath.row]
cell.textLabel?.text = contact.name
cell.detailTextLabel?.text = contact.phoneNumber
return cell
}
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text! == "" {
filteredResult = dataSource
} else {
filteredResult = dataSource.filter { $0.name.lowercased().contains(searchController.searchBar.text!.lowercased()) }
}
self.tableView.reloadData()
}
#IBAction func unwindToContactList(segue: UIStoryboardSegue) {
guard let viewController = segue.source as? AddOrEditViewController else { return }
if let name = viewController.nameTextField.text, let phoneNumber = viewController.phoneNumberTextField.text {
let contact = Contact(name: name, phoneNumber: phoneNumber)
self.dataSource.append(contact)
tableView.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "contactDetailsSegue" {
guard let viewController = segue.destination as? ContactDetialsViewController else {
return
}
guard let indexPath = tableView.indexPathForSelectedRow else { return }
let contact = self.dataSource[indexPath.row]
viewController.contact = contact
}
}
}
Are you sure that you have set the identifier for the cell on the storyboard and the identifier name is the same used on the code "ContactCell"?
I hope someone can help because this is probably a simple problem, but my Parse query within a query prints the object I am looking for, but cannot append it to the array I need it in to retrieve it in a UITableView.
The error is "fatal error: index out of range" when I dequeue the results of the query in a cell.
Here's the code:
import UIKit
import Parse
import Bolts
class MessagesTableVC: UITableViewController {
var usernames = [String]()
var sentDate = [NSDate]()
var details = [String]()
var userImage = [PFFile]()
#IBAction func backToProfile(sender: AnyObject) {
self.performSegueWithIdentifier("messagesToProfile", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
let messagesQuery = PFQuery(className: "Messages")
messagesQuery.whereKey("recipientId", equalTo: PFUser.currentUser()!.objectId!)
messagesQuery.includeKey("senderId")
messagesQuery.orderByDescending("createdAt")
messagesQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error != nil {
print(error)
}
if error == nil {
if let objects = objects {
self.usernames.removeAll(keepCapacity: true)
self.sentDate.removeAll(keepCapacity: true)
self.details.removeAll(keepCapacity: true)
self.userImage.removeAll(keepCapacity: true)
for object in objects {
self.sentDate.append(object.createdAt! as NSDate)
if (object["item"] != nil) {
self.details.append(object["item"] as! String)
} else {
self.details.append(object["request"] as! String)
}
let senderObject = (object["senderId"] as! PFUser)
let senderId = (senderObject.objectId! as String)
print(senderId)
// Query for sender info
let userQuery = PFUser.query()
userQuery?.whereKey("objectId", equalTo: senderId)
userQuery?.getFirstObjectInBackgroundWithBlock({ (object, error) in
self.usernames.append((object!["username"] as! String))
//self.userImage.append(object!["profilePicture"] as! PFFile)
})
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
//self.search.resignFirstResponder()
}
}
}
})
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return sentDate.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("messageCell", forIndexPath: indexPath) as! MessageTableViewCell
//print(usernames[indexPath.row])
cell.senderUsername.text = usernames[indexPath.row]
cell.itemOrPreview.text = details[indexPath.row]
let date = sentDate[indexPath.row]
let formatter = NSDateFormatter()
formatter.dateStyle = NSDateFormatterStyle.LongStyle
formatter.timeStyle = .ShortStyle
let dateString = formatter.stringFromDate(date)
cell.sentDate.text = dateString
//userImage[indexPath.row].getDataInBackgroundWithBlock { (data, error) in
// if let downloadedItemImage = UIImage(data: data!) {
// cell.senderImage?.image = downloadedItemImage
//}
//}
return cell
}
override func viewWillAppear(animated: Bool) {
tableView.reloadData()
}
}
As I may guess, you might get inconsistent number of elements in sentDate and usernames because you append to usernames via asynchronous getFirstObjectInBackgroundWithBlock method, so, by the time you call reloadData on tableView, all user names might not be yet added to usernames. You might have less items in usernames then in sentDate by the time your tableView callback fires and in numberOfItems you return number of items in sentDate.
In order to fix that you need at first refactor your code, it has a lot of places things might go wrong. I won't give any specific advise, but, seems, you might want to wait before you get all the data before you reload your tableView.
I have a recordtype - "DiningTypes". "DiningTypes only has one field type, which is a string. I have 5 records... and it takes 3-4 second to load a table. How is that this is so slow? Do I need to begin the fetching process in a previous controller to have quicker UI response times?
import UIKit
import CloudKit
class table1: UITableViewController {
var categories: Array<CKRecord> = []
var fetchedcategories: Array<CKRecord> = []
override func viewDidLoad() {
super.viewDidLoad()
func fetchdiningtypes()
{
let container = CKContainer.defaultContainer()
let publicDatabase = container.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "DiningTypes", predicate: predicate)
publicDatabase.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in
if (error != nil)
{
print("Error" + (error?.localizedDescription)!)
}
else
{
for result in results!
{
self.categories.append(result)
}
}
}
}
fetchdiningtypes()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return categories.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("dining") as! table1cell
let restaurant: CKRecord = categories[indexPath.row]
cell.Name.text = restaurant.valueForKey("Name") as? String
return cell
}
}
The default quality of service for CloudKit operations is NSQualityOfServiceUtility. If your application isn't in the foreground when the request runs you may see operations take longer than usual to run.
Try using CKQueryOperation and setting its qualityOfService to NSOperationQualityOfServiceUserInitiated.
I'm trying to fill a tableView with some products from my MySQL database (using a PHP POST file) and at the moment everything is fine, but when I select a "Cell", the prepareForSegue is triggered, but the indexPathForSelectedRow is wrong, so it's showing a different product.
Here is my full code, I hope you can tell me something because I don't know why is this happening, I have tried a lot of things and I'm out of options...!
TableViewController.swift
import UIKit
class ListadoBuscarResultadosTableViewController: UITableViewController {
var option: String = ""
var productos = [Producto]()
var refreshControl2:UIRefreshControl!
var imageCache = [String:UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
requestPost()
title = self.option
tableView.allowsMultipleSelection = true
tableView.scrollsToTop = true
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
func refresh(sender:AnyObject) {
requestPost()
self.refreshControl2.endRefreshing()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return productos.count
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
// Define the initial state (Before the animation)
cell.alpha = 0.25
// Define the final state (After the animation)
UIView.animateWithDuration(1.0, animations: { cell.alpha = 1 })
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// try to reuse cell
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! BuscarCellTableViewCell
cell.selectionStyle = .None
//cell.nombre.text = productos[indexPath.row].nombre
//cell.marca.text = productos[indexPath.row].marca
//println(cell.nombre.text)
// get the deal image
let currentImage = productos[indexPath.row].imagen
let unwrappedImage = currentImage
var image = self.imageCache[unwrappedImage]
let imageUrl = NSURL(string: productos[indexPath.row].imagen)
// reset reused cell image to placeholder
cell.imagen.image = UIImage(named: "")
// async image
if image == nil {
let request: NSURLRequest = NSURLRequest(URL: imageUrl!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
if error == nil {
image = UIImage(data: data)
self.imageCache[unwrappedImage] = image
dispatch_async(dispatch_get_main_queue(), {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
})
}
else {
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
})
}
else {
cell.imagen.image = image
cell.nombre.text = self.productos[indexPath.row].nombre
cell.marca.text = self.productos[indexPath.row].marca
}
return cell
}
func requestPost () {
let request = NSMutableURLRequest(URL: NSURL(string: "http://www.web.es/productos_by_category.php")!)
request.HTTPMethod = "POST"
let postString = "category="+option
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
//println("error=\(error)")
return
}
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)!
// JSON RESULTADO ENTERO
//println("responseString = \(responseString)")
self.productos = self.parseJsonData(data)
// Reload table view
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
task.resume()
}
func parseJsonData(data: NSData) -> [Producto] {
var productos = [Producto]()
var error:NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
// Return nil if there is any error
if error != nil {
println(error?.localizedDescription)
}
// Parse JSON data
let jsonProductos = jsonResult?["lista_productos"] as! [AnyObject]
for jsonProducto in jsonProductos {
let producto = Producto()
producto.nombre = jsonProducto["nombre"] as! String
producto.imagen = jsonProducto["imagen"] as! String
producto.marca = jsonProducto["marca"] as! String
producto.distribuidor = jsonProducto["distribuidor"] as! String
producto.linea = jsonProducto["linea"] as! String
producto.precio = jsonProducto["precio"] as! String
productos.append(producto)
}
return productos
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "verProducto" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let destinationController = segue.destinationViewController as! MarcaProductoViewController
//println(productos[indexPath.row].nombre)
println(indexPath)
destinationController.nombre = productos[indexPath.row].nombre
}
}
}
Thanks in advance,
Regards.
try this way...
if segue.identifier == "verProducto"{
if let indexPath = tableView.indexPathForCell(sender as! BuscarCellTableViewCell){
var detailsVC = segue.destinationViewController as! MarcaProductoViewController
}
}