I have an iOS project I'm working on using Xcode7 and Swift2. I have a TableView that fetches an array from NSCoding. I have it started so the user can reorder the TableViewCells in the TableView. However I need it to save the new order once the user is finished and clicks 'done' which is a UIBarButtonItem. I have a value in my NSCoding object called cellOrder. I looked here and saw this for CoreData. How how would I do this for NSCoding and where do I save it?
I have the following code started for the TableViewCell movement:
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true}
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let itemToMove = details[fromIndexPath.row]
details.removeAtIndex(fromIndexPath.row)
details.insert(itemToMove, atIndex: toIndexPath.row)
}
details is the array my data is kept in, using NSCoding.
I decided the best way for me was to save the details array to NSUserDefaults after the the reorder was done. I saw here how to convert the NSObject to NSData to be saved. My code for the moveRowAtIndexPath section is now:
func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let itemToMove = details[fromIndexPath.row]
details.removeAtIndex(fromIndexPath.row)
details.insert(itemToMove, atIndex: toIndexPath.row)
// Archive NSObject and save as NSData
let myData = NSKeyedArchiver.archivedDataWithRootObject(details)
// NSUserDefaults Save for Reorder of cells
reportDefaults.setObject(myData, forKey: "savedReportListKEY")
reportDefaults.synchronize()
// NSCoding Save
saveReport()
}
Related
Being a beginner with Xcode, I wanted to know if it was possible to create a list with our own components? I explain, it would be to make a list of favorites scrolling on an Xcode view, with an image, a button to delete it, and a text. It is obvious that I will have to create more components but how to put them in a list without limits?
The prototype design is as follows:
Here !
Thank you in advance, Sincerely.
Yes you can make your own list cells. For that you will need to,
create your own UITableViewCell and add your componenents (2x UIImageViews, 1x UILabel)
register your cell to the UITableView you want to display the cell in with the identifier
Conform to both UITableViewDelegate & UITableViewDataSource
Refer this blog to learn more as I barely scratched the surface with the explanation
You can add data to the table as follows
/// Conforming to the UITableViewDelegate, UITableViewDataSource protocols
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// Table view to display data
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// setting the datasource and delegate
tableview.delegate = self
tableview.dataSource = self
}
/// 2d Array to store data
let data = [["Melon", "Glace"], ["Epinard"], ["one", "two", "three"] ]
func numberOfSections(in tableView: UITableView) -> Int {
return self.data.count
}
/// UITableViewDataSource protocol stubs
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as? CustomTableViewCell {
cell.name = data[indexPath.section][indexPath.row]
return cell
}
return UITableViewCell()
}
}
I am using the following method to move between viewControllers:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "segueJokeDetail", sender: self)
}
That's the capture from the storyboard showing segueId
If you do a segue from a UITableViewCell in interface builder, you don’t need to do it in the UITableView delegate as well.
Ive been at this for a while but cant seem to crack it in swift
I want a user to be able to select an image in uicollectionView and for that image to appear in a detailed view controller, i can do this quite easily with a peice of text,
and i can do this when there is a static array of images preloaded. but i cant seem to get anywhere with a collectionview which is loaded with images from a camera.
I understand i need to use
override func performSegueWithIdentifier(identifier: String, sender: AnyObject?) {
}
and this function to isolated selected cell.
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
}
I do have these outlets
#IBOutlet weak var collectionView: UICollectionView!
var images = [UIImage]()
image picker stores all images to this array by
images.insert(newImage, atIndex: 0)
when the array would be passed to the detailviewcontroller, i understand that would have to be copied into another local array and then how would i get the current image that was highlighted to be shown first, perhaps using indexPath.Row
Regards
I'm not using segues, and actually I don't quite understand what your problem is, but I'll try to show you how it could be achieved.
First of all, you have an array with images, so I believe your image should be accessed as images[indexPath.row]
let's suppose that you already have UIViewController class with UIImageView in it.
if so, you can write something like that:
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.presentViewController(myVC, animated: true, completion: nil)
}
for modal or
func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = MyViewController()
myVC.imageView.image = images[indexPath.row]
self.navigationController?.pushViewController(myVC, animated: true)
}
if you want to show it as navigational.
I believe that with segues it's basically the same but I think you have to use func prepareForSegue for, you know, preparing the segue (func performSegueWithIdentifier will just perform it). In prepareForSegue you should check identifier of your segue
if segue.identifier == "myIdentifier" {
//your code here
}
and if this identifier is right, put your commands to your myVC there.
In my TableViewController i have this line of code
override func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
In this block i try to get a list of objects
let list = frc.objectAtIndexPath(indexPath) as! List
But i get this warning:
Use of undeclared type "List"
Anybody knows how to deal with it?
This is the full code:
var frc: NSFetchedResultsController = NSFetchedResultsController()
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",
forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
let list = frc.objectAtIndexPath(indexPath) as! list
cell.textLabel?.text = list.name
cell.detailTextLabel?.text = list.url
return cell
}
Any help is welcome!!
Greetings and Thanks
In the tutorial, Jason creates an entity called "List" in his Data Model file and then creates a class called List.
The following link starts the video just after he creates this file.
https://youtu.be/GeM7Zw12wbM?t=5m30s
You may have named your entity differently.
I named mine Record, since I'm storing records.
So my code will look like the following:
var frc: NSFetchedResultsController = NSFetchedResultsController()
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",
forIndexPath: indexPath) as! UITableViewCell
// Configure the cell...
// ALL occurrences of "list" are instead "record"
// ALL occurrences of "List" are instead "Record"
let record = frc.objectAtIndexPath(indexPath) as! Record
cell.textLabel?.text = record.name
cell.detailTextLabel?.text = record.url
return cell
}
I am trying to set the height of each row in the tableView to the height of the corresponding cell with this code:
override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
var cell = tableView.cellForRowAtIndexPath(indexPath)
return cell.frame.height
}
I get this error when initialising var cell :
Thread 1:EXC_BAD_ACCESS(code=2,address=0x306d2c)
For setting row height there is separate method:
For Swift 3
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0;//Choose your custom row height
}
Older Swift uses
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100.0;//Choose your custom row height
}
Otherwise you can set row height using:
self.tableView.rowHeight = 44.0
In ViewDidLoad.
Put the default rowHeight in viewDidLoad or awakeFromNib. As pointed out by Martin R., you cannot call cellForRowAtIndexPath from heightForRowAtIndexPath
self.tableView.rowHeight = 44.0
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
var height:CGFloat = CGFloat()
if indexPath.row == 1 {
height = 150
}
else {
height = 50
}
return height
}
yourTableView.rowHeight = UITableViewAutomaticDimension
Try this.
As pointed out in comments, you cannot call cellForRowAtIndexPath inside heightForRowAtIndexPath.
What you can do is creating a template cell used to populate with your data and then compute its height.
This cell doesn't participate to the table rendering, and it can be reused to calculate the height of each table cell.
Briefly, it consists of configuring the template cell with the data you want to display, make it resize accordingly to the content, and then read its height.
I have taken this code from a project I am working on - unfortunately it's in Objective C, I don't think you will have problems translating to swift
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static PostCommentCell *sizingCell = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sizingCell = [self.tblComments dequeueReusableCellWithIdentifier:POST_COMMENT_CELL_IDENTIFIER];
});
sizingCell.comment = self.comments[indexPath.row];
[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];
CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height;
}
Problem Cause:
The problem is that the cell has not been created yet. TableView first calculates the height for row and then populates the data for each row, so the rows array has not been created when heightForRow method gets called. So your app is trying to access a memory location which it does not have the permission to and therefor you get the EXC_BAD_ACCESS message.
How to achieve self sizing TableViewCell in UITableView:
Just set proper constraints for your views contained in TableViewCell's view in StoryBoard. Remember you shouldn't set height constraints to TableViewCell's root view, its height should be properly computable by the height of its subviews -- This is like what you do to set proper constraints for UIScrollView. This way your cells will get different heights according to their subviews. No additional action needed
Make sure Your TableView Delegate are working as well.
if not then
in your story board or in .xib
press and hold Control + right click on tableView drag and Drop to your Current ViewController.
swift 2.0
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 60.0;
}
There is no way to call tableView.dequeueReusableCell from within tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath).
So you have to compute the height from the data.
In my case that was not possible because I have a textView in the cell, which size I did not know.
So I come around with the following strategies:
Do not use tableView.dequeueReusableCell. Just use an array of cells yo have under full control. If your cell is not too large in memory and you don't have too much rows, this is the simplest strategy.
Use a dummy cell, configure it with your data and compute the size.
Try code like this copy and paste in the class
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}