I have a problem with my ViewController.
My code has an error about initializers and I can't understand why.
Please, take a moment to look at my code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
let sectionsTableIdentifier = "SectionsTableIdentifier"
var names: [String: [String]]!
var keys: [String]!
#IBOutlet weak var tableView: UITableView!
var searchController: UISearchController
//methods
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: sectionsTableIdentifier)
let path = NSBundle.mainBundle().pathForResource("sortednames", ofType: "plist")
let namesDict = NSDictionary(contentsOfFile: path!)
names = namesDict as! [String: [String]]
keys = namesDict!.allKeys as! [String]
keys = keys.sort()
let resultsController = SearchResultsController()
resultsController.names = names
resultsController.keys = keys
searchController = UISearchController(searchResultsController: resultsController)
let searchBar = searchController.searchBar
searchBar.scopeButtonTitles = ["All", "Short", "Long"]
searchBar.placeholder = "Enter a search term"
searchBar.sizeToFit()
tableView.tableHeaderView = searchBar
searchController.searchResultsUpdater = resultsController
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return keys.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let key = keys[section]
let nameSection = names[key]!
return nameSection.count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return keys[section]
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(sectionsTableIdentifier, forIndexPath: indexPath) as UITableViewCell
let key = keys[indexPath.section]
let nameSection = names[key]!
cell.textLabel!.text = nameSection[indexPath.row]
return cell
}
func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
return keys
}
}
What is the problem?
The error is that the class has no initializer. I have no variables with no value.
Problematic line is
var searchController: UISearchController
Change it to
var searchController: UISearchController!
or if you are not initializing it in view life cycles, use optional to avoid crashes:
var searchController: UISearchController?
Your line which catch the error is:
var searchController: UISearchController
because you never init searchController in a LifeCycle init function from your UIViewController. I advice you not to force unwrap the var (like Sahil said above) but to properly init it into an init func like this:
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
func setUp() {
searchController = UISearchController() //Or any init you can use to perform some custom initialization
}
In Swift, you always should avoid force unwrap Object like above, to avoid crash in your app, or use if-Let/Guard-Let template
Cheers from France
Related
I have a program written in Swift 3, that grabs JSON from a REST api and appends it to a table view.
Right now, I'm having troubles with getting it to print in my Tableview, but it does however understand my count function.
So, I guess my data is here, but it just doesn't return them correctly:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, HomeModelProtocal {
#IBOutlet weak var listTableView: UITableView!
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
var feedItems: NSArray = NSArray()
var selectedLocation : Parsexml = Parsexml()
override func viewDidLoad() {
super.viewDidLoad()
self.listTableView.delegate = self
self.listTableView.dataSource = self
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "BasicCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: Parsexml = feedItems[indexPath.row] as! Parsexml
myCell.textLabel!.text = item.title
return myCell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return feedItems.count
}
override func viewDidAppear(_ animated: Bool) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Are you by any chance able to see the error that I can't see?
Note. I have not added any textlabel to the tablerow, but I guess that there shouldn't be added one, when its custom?
Try this code:
override func viewDidLoad() {
super.viewDidLoad()
print(yourArrayName.count) // in your case it should be like this print(feedItems.count)
}
So I am using a searchResultsController, which takes an array of Strings, and shows them in a tableview (It's an autocomplete list). When the user presses the 'Search' button on the keyboard, and the entered String is not yet in my Tableview, I want to add it, and update the tableview accordingly.
The issue is that once I added a String to the array, and make a new search, the array isn't updated with the new value!
Here is my code:
In my ViewDidLoad() on the Overview.swift class
class Overview: UIViewController,UISearchControllerDelegate,UISearchBarDelegate,UICollectionViewDelegate,UICollectionViewDataSource {
var mySearchController : UISearchController!
var mySearchBar : UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
let src = SearchResultsController(data: convertObjectsToArray())
// instantiate a search controller and keep it alive
mySearchController = UISearchController(searchResultsController: src)
mySearchController.searchResultsUpdater = src
mySearchBar = mySearchController.searchBar
//set delegates
mySearchBar.delegate = self
mySearchController.delegate = self
}
This is the data function, used for the UISearchController
func convertObjectsToArray() -> [String] {
//open realm and map al the objects
let realm = try! Realm()
let getAutoCompleteItems = realm.objects(AutoComplete).map({$0})
...
return convertArrayStrings // returns [String] with all words
}
So when the user pressed the search button on the keyboard, I save that word to my database.
Now I need to put the updated version of convertObjectsToArray() in my searchResultsController, but I haven't found out how to do this. All help is welcome
And last, but not least, my SearchResultsController class, which is used in the viewDidLoad of my Overview.swift class.
class SearchResultsController : UITableViewController {
var originalData : [String]
var filteredData = [String]()
init(data:[String]) {
self.originalData = data
super.init(nibName: nil, bundle: nil)
}
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.filteredData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel!.text = self.filteredData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
clickedInfo = filteredData[indexPath.row]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
}
For the filtering of my words in the tableview (when user types something, only matching Strings are shown), I use the following extension.
extension SearchResultsController : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
let sb = searchController.searchBar
let target = sb.text!
self.filteredData = self.originalData.filter {
s in
let options = NSStringCompareOptions.CaseInsensitiveSearch
let found = s.rangeOfString(target, options: options)
return (found != nil)
}
self.tableView.reloadData()
}
You can use the search controller's update function for that I think:
func updateSearchResultsForSearchController(searchController: UISearchController) {
convertObjectsToArray()
self.tableView.reloadData()
}
I create uiSegmentedControl in HomePageViewController. There are two items in segmented control. When I select first item , I add sensorItemViewController content as a subview in HomePageViewController with displayContentController method. And when clicked second item, I want to access methods of SensorTabItemViewController class which it's name is reloadMyTableView from HomePageViewConroller. I accessed from sensorItemVC but I get "unexpectedly found nil while unwrapping an Optional value" exception. How can access SensorItemTabViewController from HomePageViewControler correctly ? Thank you all response
HomePageViewController.swift :
let segmentedControlItems = ["Table", "RefreshTableView"]
var viewControllerArray: Array<UIViewController> = []
var segmentedControl : UISegmentedControl!
var sensorItemVC: SensorTabItemViewController!
class HomePageViewController: UIViewController,UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
segmentedControl=UISegmentedControl(items: segmentedControlItems)
segmentedControl.selectedSegmentIndex=0
segmentedControl.tintColor=myKeys.darkBlueColor
segmentedControl.layer.cornerRadius = 0.0;
segmentedControl.layer.borderWidth = 1.5
segmentedControl.frame=CGRectMake(0, frameHeight/2, frameWidth, 35)
segmentedControl.addTarget(self, action: "changeSegmentedControlItem", forControlEvents: .ValueChanged)
self.view.addSubview(segmentedControl)
let sensorItemViewController = self.storyboard!.instantiateViewControllerWithIdentifier("sensorTabItemViewController")
viewControllerArray.append(sensorItemViewController)
}
func changeSegmentedControlItem(){
print(segmentedControl.selectedSegmentIndex)
if(segmentedControl.selectedSegmentIndex==0){
displayContentController(viewControllerArray[0])
}
else{
sensorItemVC.reloadMyTableView("Temp value", light: "Light value", noise: "noise Value", motion: "motion Value")
}
}
func displayContentController (content:UIViewController) {
self.addChildViewController(content)
print(self.segmentedControl.frame.height)
content.view.frame=CGRectMake(0, self.frameHeight/2+self.segmentedControl.frame.height, self.frameWidth,
self.frameHeight-(segmentedControl.frame.height*2+self.frameHeight/2))
self.view.addSubview(content.view)
content.didMoveToParentViewController(self)
}
}
SensorTabItemViewController. swift as below :
class SensorTabItemViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
let sensorName=["Sıcaklık Sensörü","Işık Sensörü","Gürültü Sensörü","Hareket Sensörü"]
var sensorDetails=["","","",""]
var sensorImages: Array<UIImage> = []
override func viewDidLoad() {
super.viewDidLoad()
print("sensorTab")
let tempImg=UIImage(named: "temp_ic") as UIImage?
let lightImg=UIImage(named: "light_ic") as UIImage?
let noiseImg=UIImage(named: "noise_ic") as UIImage?
let motionImg=UIImage(named: "motion_ic") as UIImage?
sensorImages.append(tempImg!)
sensorImages.append(lightImg!)
sensorImages.append(noiseImg!)
sensorImages.append(motionImg!)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func reloadTableView(){
sensorDetails=[]
sensorDetails.append(temp)
sensorDetails.append(light)
sensorDetails.append(noise)
sensorDetails.append(motion)
tableView.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sensorName.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("sensorCell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text=sensorName[indexPath.row]
cell.imageView?.image=sensorImages[indexPath.row]
cell.detailTextLabel?.text=sensorDetails[indexPath.row]
return cell
}
}
You never set the value of sensorItemVC. That is why it is nil. I guess that
let sensorItemViewController = self.storyboard!.instantiateViewControllerWithIdentifier("sensorTabItemViewController")
should be replaced with
sensorItemVC = self.storyboard!.instantiateViewControllerWithIdentifier("sensorTabItemViewController") as! SensorTabItemViewController
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let path = NSBundle.mainBundle().pathForResource("TableRowInfo", ofType: "plist")!
let dict = NSDictionary(contentsOfFile:path)!
var artists: AnyObject = dict.objectForKey("Artist")!
var stages: AnyObject = dict.objectForKey("Stage")!
println(artists)
println(stages)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return artists.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("InfoCell", forIndexPath: indexPath) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "InfoCell")
cell!.accessoryType = .DisclosureIndicator
}
return cell!
}
}
Hey all,
I'm new to swift so just experimenting with some things. What I'm trying to do is filling my table with content from a plist file. I know this isn't the best way! I already loaded the list successfully. My println(artists) returns what I want as well does the stages. The only problem is if I call artists or stages outside my viewDidLoad function it doesn't work. Why is that and how do I solve it?
Thanks in advance.
Greets,
Wouter
The variables "artists" and "stages" do not exists outside of the viewDidLoad-Function scope. You have to define them as properties to access them outside of the viewDidLoad-function. Like this
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var artists: AnyObject?
var stages: AnyObject?
override func viewDidLoad() {
...
artists: AnyObject = dict.objectForKey("Artist")
...
}
I'm trying to get location data(PFGeoPoint) from parse.com, show it in UITableView, and sort it by nearest one from user location.
I already use the code same with shown in parse documentation :
findPlaceData.whereKey("position", nearGeoPoint:SearchLocationGeoPoint)
I managed to get the data. I also managed to show it in my UITableView. The problem is, the order is reversed. I got the farthest in my first cell. Could anyone explain why this happen, and how to fix it?
import UIKit
class SearchTableViewController: UITableViewController {
#IBOutlet var SearchTitle: UILabel!
var userLocationToPass:CLLocation!
var categoryToPass:String!
var categoryIdToPass:String!
var placeData:NSMutableArray! = NSMutableArray()
override init(style: UITableViewStyle) {
super.init(style: style)
// Custom initialization
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func loadData(){
placeData.removeAllObjects()
let searchLocationGeoPoint = PFGeoPoint(location: userLocationToPass)
var findPlaceData:PFQuery = PFQuery(className: "Places")
findPlaceData.whereKey("category", equalTo: categoryIdToPass)
findPlaceData.whereKey("position", nearGeoPoint:searchLocationGeoPoint)
findPlaceData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
let place:PFObject = object as PFObject
self.placeData.addObject(place)
}
let array:NSArray = self.placeData.reverseObjectEnumerator().allObjects
self.placeData = NSMutableArray(array: array)
self.tableView.reloadData()
}
}
}
override func viewWillAppear(animated: Bool) {
loadData()
}
override func viewDidLoad() {
super.viewDidLoad()
SearchTitle.text = categoryToPass
println(userLocationToPass)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return placeData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:SearchTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as SearchTableViewCell
let place:PFObject = self.placeData.objectAtIndex(indexPath.row) as PFObject
cell.placeName.text = place.objectForKey("name") as? String
cell.placeOpenHour.text = place.objectForKey("openhour") as? String
return cell
}
}
Are you intentionally using the reverseObjectEnumerator? Because that could account for your results being reversed... The clue is in the method name ;-)
If you drop the following two lines from your code, it might not be reversed anymore.
let array:NSArray = self.placeData.reverseObjectEnumerator().allObjects
self.placeData = NSMutableArray(array: array)