Xcode Debugger: fatal error: Array index out of range.. why? - ios

I'm getting a fatal error: Array index out of range error when i run my application, but I don't see why. Here is my code:
var rippleLocations: [MKRippleLocation] = [.TapLocation, .TapLocation, .Center, .Left, .Right, .TapLocation, .TapLocation, .TapLocation]
var circleColors = [UIColor.clearColor(), UIColor.clearColor(),UIColor.clearColor(),UIColor.clearColor()]
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aSport.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MKTableViewCell
cell.textLabel?.text = aSport[indexPath.row].name
cell.textLabel?.text = aSport[indexPath.row].name
cell.backgroundColor = UIColor.clearColor()
cell.textLabel?.font = UIFont(name: "HelveticaNeue-Thin", size: 16)
cell.textLabel?.textColor = UIColor.whiteColor()
cell.rippleLocation = rippleLocations[indexPath.row]
let index = indexPath.row % circleColors.count
cell.rippleLayerColor = circleColors[index]
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let cell = sender as! MKTableViewCell
let row = tableView.indexPathForCell(cell)?.row
let detail = segue.destinationViewController as! SecondTableViewController
detail.selectedSchool = aSport[row!]
}
The error is highlighted on the cell.rippleLocation = rippleLocations[indexPath.row] string.. why is this error here?

The number of rows is equal to aSport.count:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aSport.count
}
Shouldn't that be
return rippleLocations.count
? Or are you sure that aSport and rippleLocations always have the same number of elements? If you want to cycle through the ripple locations, replace
cell.rippleLocation = rippleLocations[indexPath.row]
with
cell.rippleLocation = rippleLocations[indexPath.row % rippleLocations.count]

It seems like the size of the rippleLocations arrays is less than aSport ...
So for example at the 10th row it will crash if aSport has 10 objects and rippleLocations has 8 objects only.
becuase in numberofRows method you're returning aSport's count

Related

Swift TableView insert row below button clicked

I am new to Swift and I am using Swift 4.2 . I have a TableView with a label and button . When I press a button I would like to add a new row directly below the row in which the button was clicked . Right now when I click a button the new row gets added to the bottom of the TableView every time. I have been looking at posts on here but haven't been able to get it working this is my code base . I have a method called RowClick I get the indexpath of the row that was clicked but do not know how to use that to get the new row to appear directly below the clicked row .
class ExpandController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var TableSource: UITableView!
var videos: [String] = ["FaceBook","Twitter","Instagram"]
override func viewDidLoad() {
super.viewDidLoad()
TableSource.delegate = self
TableSource.dataSource = self
TableSource.tableFooterView = UIView(frame: CGRect.zero)
// Do any additional setup after loading the view.
}
#IBAction func RowClick(_ sender: UIButton) {
guard let cell = sender.superview?.superview as? ExpandTVC else {
return
}
let indexPath = TableSource.indexPath(for: cell)
InsertVideoTitle(indexPath: indexPath)
}
func InsertVideoTitle(indexPath: IndexPath?)
{
videos.append("Snapchat")
let indexPath = IndexPath(row: videos.count - 1, section: 0)
TableSource.beginUpdates()
TableSource.insertRows(at: [indexPath], with: .automatic)
TableSource.endUpdates()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let videoTitle = videos[indexPath.row]
let cell = TableSource.dequeueReusableCell(withIdentifier: "ExpandTVC") as! ExpandTVC
cell.Title.text = videoTitle
cell.ButtonRow.tag = indexPath.row
cell.ButtonRow.setTitle("Rows",for: .normal)
return cell
}
}
This is how my table looks I clicked the Facebook Rows button and it appended the string SnapChat . The Snapchat label should appear in a row below Facebook instead . Any suggestions would be great !
I think the easiest solution without re-writing this whole thing would be adding 1 to the current row of the IndexPath you captured from the action.
let indexPath = TableSource.indexPath(for: cell)
var newIndexPath = indexPath;
newIndexPath.row += 1;
InsertVideoTitle(indexPath: newIndexPath);
I did this from memory because I am not near an IDE, so take a look at the change and apply that change if needed in any other location.
class ExpandController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var TableSource: UITableView!
var videos: [String] = ["FaceBook","Twitter","Instagram"]
override func viewDidLoad() {
super.viewDidLoad()
TableSource.delegate = self
TableSource.dataSource = self
TableSource.tableFooterView = UIView(frame: CGRect.zero)
// Do any additional setup after loading the view.
}
#IBAction func RowClick(_ sender: UIButton) {
guard let cell = sender.superview?.superview as? ExpandTVC else {
return
}
let indexPath = TableSource.indexPath(for: cell)
var newIndexPath = indexPath;
newIndexPath.row += 1;
InsertVideoTitle(indexPath: newIndexPath);
}
func InsertVideoTitle(indexPath: IndexPath?)
{
videos.append("Snapchat")
let indexPath = IndexPath(row: videos.count - 1, section: 0)
TableSource.beginUpdates()
TableSource.insertRows(at: [indexPath], with: .automatic)
TableSource.endUpdates()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let videoTitle = videos[indexPath.row]
let cell = TableSource.dequeueReusableCell(withIdentifier: "ExpandTVC") as! ExpandTVC
cell.Title.text = videoTitle
cell.ButtonRow.tag = indexPath.row
cell.ButtonRow.setTitle("Rows",for: .normal)
return cell
}
}
Your current code calls append to add the new item at the end of the array. What you want to do is insert a new row at indexPath.row+1. Array has an insert(element,at:) function.
You have to handle the case where the user has tapped the last row and not add 1 to avoid an array bounds error:
func InsertVideoTitle(indexPath: IndexPath)
{
let targetRow = indexPath.row < videos.endIndex ? indexPath.row+1 : indexPath.row
videos.insert("Snapchat" at:targetRow)
let newIndexPath = IndexPath(row: targetRow, section: 0)
TableSource.beginUpdates()
TableSource.insertRows(at: [newIndexPath], with: .automatic)
TableSource.endUpdates()
}

Leave A Comment With Json Wordpress in Xcode

i made app with Xcode in swift using my Wordpress backend and getting data with json, i can show users comment on my app but i want that users can also leave comments in the app, there should be an option for Leave a comment on post details file, this is my code for showing comments
#IBAction func commentViewController(_ sender: Any) {
let vc = CommentViewController()
vc.dataArray = jsonData["comments"].array
self.navigationController!.pushViewController(vc, animated: true)
}
self.commentButton.layer.borderWidth = 2
self.commentButton.layer.borderColor = baseColor.cgColor
self.commentButton.layer.cornerRadius = 4.0
self.commentButton.tintColor = baseColor
self.commentButton.setTitle("comments(\(jsonData["comments"].array?.count ?? 0))", for: .normal)
but i don't know how to make a comment box where users can also leave comment , thanks
This Is My CommentViewController:
var tableView: UITableView = UITableView()
var dataArray: Array<JSON>!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Comments"
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .automatic
let attributes = [
NSAttributedString.Key.foregroundColor : navigationBarTextColor, NSAttributedString.Key.font: UIFont(name: "SFUIText-Medium", size: 34),
]
navigationController?.navigationBar.largeTitleTextAttributes = attributes as [NSAttributedString.Key : Any]
} else {
// Fallback on earlier versions
}
// Check if Post have comments
if(self.dataArray.count == 0){
self.view.backgroundColor = UIColor(red: 216 / 255, green: 216 / 255, blue: 216 / 255, alpha: 1.0)
let label = UILabel(frame: CGRect(x: (UIScreen.main.bounds.width/2)-100, y: (UIScreen.main.bounds.height/2)-50, width: 200, height: 50))
label.text = "No Comments"
label.font = UIFont(name: "SFUIText-Regular", size: 18)
label.textAlignment = NSTextAlignment.center
self.view.addSubview(label)
}
else{
self.setupTable()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getDateFromString(String:String) -> String{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateObj = dateFormatter.date(from: String)
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.string(from: dateObj!)
}
func setupTable(){
tableView = UITableView(frame: UIScreen.main.bounds, style: UITableView.Style.plain)
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "commentViewCell", bundle: nil), forCellReuseIdentifier: "cell")
tableView.frame.size.height = UIScreen.main.bounds.height - 64
tableView.bounces = false
tableView.allowsSelection = false
self.view.addSubview(self.tableView)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! commentViewCell
cell.commentText.text = String(htmlEncodedString: dataArray[indexPath.row]["content"].stringValue)
cell.name.text = String(htmlEncodedString:dataArray[indexPath.row]["name"].stringValue)
cell.date.text = getDateFromString(String: dataArray[indexPath.row]["date"].stringValue)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
So you have a "tableView" field where all comments are shown.
As Joakim Danielson advices, you should add UITextView to the bottom of your screen.
Because the table view can be too long to fit the screen height, it probably will be scrolled, so it's easier to add this text view as a cell.
So you can do this in a following way:
Change the size of tableView for leaving the comment at the end:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.dataArray.count + 1
}
Add the cell with text view, let's name it LeaveCommentCell
Return the LeaveCommentCell instance as the last cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.row == self.dataArray.count { // this is the last row
let cell = tableView.dequeueReusableCell(withIdentifier: "AddCommentCell", for: indexPath) as! LeaveCommentCell // your new cell
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "ShowCommentCell", for: indexPath) as! commentViewCell // please note identifiers should differ
cell.commentText.text = String(htmlEncodedString: dataArray[indexPath.row]["content"].stringValue)
cell.name.text = String(htmlEncodedString:dataArray[indexPath.row]["name"].stringValue)
cell.date.text = getDateFromString(String: dataArray[indexPath.row]["date"].stringValue)
return cell
}
Add a button (e.g. with text "Post") to LeaveCommentCell, subscribe for event when it's pressed, get comment text and send it to server.

DidSelectRow is not working

Pretty much what the title says: DidSelectRow is not getting called, and I did check that I didn't use Deselect. I also checked that delegate and DataSource are connected to the tableViewController and the Selection was Single Selection. However, the method is still not getting called.
Does an empty tableView might have any effect to it(reason it's empty is because the user starts to populate it. I'm using CoreData for it)?
EDIT
This is what the method looks like:
var effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
var effect: UIVisualEffect!
let defaults = UserDefaults.standard
var userMagazineTitle = [User]()
var dataString = String()
override func viewDidLoad() {
super.viewDidLoad()
addProgrammatically()
let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
do{
let users = try PresistanceService.context.fetch(fetchRequest)
self.userMagazineTitle = users
self.tableView.reloadData()
}catch{
ProgressHUD.showError("Nothing to see here")
}
}
/ MARK: - Table view data source
override func numberOfSections(in 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 userMagazineTitle.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyFeedTableViewCell
cell.myHeadline?.text = userMagazineTitle[indexPath.row].title
cell.indentationLevel = 3
return cell
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
let manage = PresistanceService.persistentContainer.viewContext
let del = userMagazineTitle[indexPath.row]
if editingStyle == .delete {
// Delete the row from the data source
manage.delete(del)
do{
try manage.save()
}catch{
ProgressHUD.showError()
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
do{
userMagazineTitle = try manage.fetch(fetchRequest) as! [User]
}catch{
ProgressHUD.showError()
}
tableView.reloadData()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showUser", sender: indexPath)
navigationController?.navigationBar.isHidden = false
tableView.deselectRow(at: indexPath, animated: true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showUser" {
let DestViewController = segue.destination as! UINavigationController
let targetController = DestViewController.topViewController as! CompanyTableViewController
let indexPath = sender as! IndexPath
let user = userMagazineTitle[indexPath.row].title
let user2 = userMagazineTitle[indexPath.row].rssurl
targetController.customUserInit(articlesindex: indexPath.row, title: user!, rssUrl: user2!)
}
}
func addProgrammatically() {
effectView.frame = view.bounds
tableView.addSubview(effectView)
effectView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
effectView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
} else {
// Fallback on earlier versions
effectView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
}
effectView.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
effectView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
effect = effectView.effect
effectView.effect = nil
self.tableView.separatorStyle = .none
tableView.backgroundView = UIImageView(image: UIImage(named: "myMagazines.jpg"))
tableView.backgroundView?.contentMode = .scaleAspectFill
tableView.clipsToBounds = true
navigationController?.navigationBar.isHidden = false
UIApplication.shared.statusBarStyle = .lightContent
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
}
}
Another thing that might lead to the issue is not selected selection kind. I lost my two hours for fixing this.
Cross check cell selection , if it is selected for no selection change it to single selection.
To do this programatically :
Swift : tableView.allowsSelection = YES;
Objective C: [tableView allowsSelection : YES];
==============================
This might be helpful to someone!
You have added a UIVisualEffectView view on top of tableview, which is not passing the touch events to tableview.
Setting effectView.isUserInteractionEnabled = false, should make this view pass touch events to the views underneath.
There could be two issues for this problem:
The method signature of the TableView DidSelectRow could be wrong. The current signature is:
Swift:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
ObjectiveC: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
You are setting the Delegate of the TableView to nil somewhere in the code.

Value of type '_' has no member

In the cb.check(self.rowChecked[indexPath.row]) line under cellForRowAt I'm getting a "Value of type 'LolFirstTableViewController' has no member 'rowChecked'" even though I set up rowChecked to be an array of Booleans with tasks.count number of items. Do I need to initialize rowChecked somewhere else besides cellForRowAt or what am I doing wrong here? The point of this code is to make a checkbox show up in each cell of a table where you can click it to change the accessory to a check mark, and click it again to uncheck it. The check box itself is a separate custom class called CheckButton. I'm still learning Swift so any help would be greatly appreciated! Thank you!
import UIKit
class LoLFirstTableViewController: UITableViewController {
var tasks:[Task] = taskData
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
#IBAction func cancelToLoLFirstTableViewController(_ segue:UIStoryboardSegue) {
}
#IBAction func saveAddTask(_ segue:UIStoryboardSegue) {
if let AddTaskTableViewController = segue.source as? AddTaskTableViewController {
if let task = AddTaskTableViewController.task {
tasks.append(task)
let indexPath = IndexPath(row: tasks.count-1, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
var rowChecked: [Bool] = Array(repeating: false, count: tasks.count)
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(self.rowChecked[indexPath.row])
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
var tappedItem = tasks[indexPath!.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath!.row] = tappedItem
tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none)
}
You are declaring rowChecked as a local variable and calling it with self.rowChecked as if it were a class property.
To solve this issue, remove the self. before rowChecked.
Old:
cb.check(self.rowChecked[indexPath.row])
New:
cb.check(rowChecked[indexPath.row])
There might be further issues, but that's the reason for the error as your code currently stands.
You have the line: var rowChecked: [Bool] = Array(repeating: false, count: tasks.count) inside the tableView:cellForRowAt method, so it's a local variable, it's not a property of the LolFirstTableViewController class.
That means you need to change this line: cb.check(self.rowChecked[indexPath.row]) to cb.check(rowChecked[indexPath.row]) (Removed self.).

Setting rowHeight equal to UITableViewAutomaticDimension not working

I'm using XCode 6.3 to build a TableView of the different Fonts in iOS 8. First, per the book I'm reading, it said that nothing needed to be done regarding the height of the table rows, given that iOS8 takes care of that for you, so once I had everything per the book, the rows should update their heights based on their content, which wasn't the case. Then I tried to play with tableView.rowHeight and I set it equal to UITableViewAutomaticDimension in the TableViewController's viewDidLoad function, and that didn't work either. I also tried changing the height of the rows from Interface Builder, and that didn't seem to have any effect on the heights either. My code is as follows:
class RootViewController: UITableViewController {
private var familyNames: [String]!
private var cellPointSize: CGFloat!
private var favoritesList: FavoritesList!
private let familyCell = "FamilyName"
private let favoritesCell = "Favorites"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
familyNames = sorted(UIFont.familyNames() as! [String])
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
favoritesList = FavoritesList.sharedFavoritesList
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont? {
if indexPath.section == 0 {
let familyName = familyNames[indexPath.row]
let fontName = UIFont.fontNamesForFamilyName(familyName).first as! String
return UIFont(name: fontName, size: cellPointSize)
} else {
return nil
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return favoritesList.favorites.isEmpty ? 1 : 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return section == 0 ? familyNames.count : 1
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "All Font Families" : "Favorite Fonts"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier(familyCell, forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel!.text = familyNames[indexPath.row]
cell.detailTextLabel!.text = familyNames[indexPath.row]
return cell
} else {
return tableView.dequeueReusableCellWithIdentifier(favoritesCell, forIndexPath: indexPath) as! UITableViewCell
}
}
}
When I run this in the simulator, everything looks right until I scroll all the way to the bottom and I get this:
The attributes of the FontFamily cell are: style = subtitle, and accessory = disclosure indicator.
Any ideas on what I'm be doing wrong?
You must set self.tableView.estimatedRowHeight

Resources