Detail view data confusing when peek - swift

Im trying to implement 3D touch support for my app and I'm encountering some issues at the moment
//3D Touch - Peek
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
guard let indexPath = tableView.indexPathForRow(at: location),
let cell = tableView.cellForRow(at: indexPath) as? CustomCell else {return nil}
let identifier = "TemplateTestViewController"
guard let BarVC = storyboard?.instantiateViewController(withIdentifier: identifier) as?
TemplateTestViewController else { return nil }
//Set Ups the Image Template :
BarVC.HeaderprofileImage?.image = UIImage(named: ProfileImages[indexPath.row] + "jpg")
//Sets Up Bar Or Club Text :
BarVC.BarOrClubLabel?.text = ProfileBarOrClub[MyIndex]
//Sets Up Age Text :
BarVC.AgeLabel.text = AgeText[MyIndex]
//Sets Up Phone Number Text :
BarVC.ProfilePhoneNumberLabel?.setTitle(ProfilePhoneNumbers[indexPath.row], for: .normal)
if #available(iOS 9.0, *) {
previewingContext.sourceRect = cell.frame
} else {
// Fallback on earlier versions
}
return BarVC
}
As you can see I'm trying to change the data between each cells with MyIndex which is basically indexPath.row. I don't get any errors and yet when i run the app the data isn't changed between different cells being peeked...
any help will be really appreciated !

Related

Modify cell contents in tableview after clicking in Swift

I have a chat app with 2 custom cells, sender and receiver cell. Once the cells are populated, only the ones with "show image" text can be clicked. I am trying to click on these cells so that
i can hide the label
I can show the image from the url
The tap function works but i am not able to do the above steps. Kindly help because i do not know how to go about this
Here is my code
#objc
func tapFunctionCell(sender:UITapGestureRecognizer) {
print("tap again sen")
let tapLocation = sender.location(in: tblViewChat)
let indexPath = self.tblViewChat.indexPathForRow(at: tapLocation)
let position = indexPath?.row ?? 0
print(position)
let cell = tblViewChat.dequeueReusableCell(withIdentifier: "senderCell") as! SenderTblCell
cell.lblMessage.isHidden = true
let url = URL(string:objChatVM.getMessage(index:position))
print(url)
cell.ImageB.kf.setImage(with:url)
cell.ImageB.isHidden = false
}
#objc
func tapFunction(sender:UITapGestureRecognizer) {
print("tap again receive")
let cell2 = tblViewChat.dequeueReusableCell(withIdentifier: "receiverCell") as! ReceiverTblCell
cell2.lblMessage.isHidden = false
let tapLocation = sender.location(in: tblViewChat)
let indexPath = self.tblViewChat.indexPathForRow(at: tapLocation)
let position = indexPath?.row ?? 0
print(position)
let url = URL(string:objChatVM.getMessage(index:position))
print(url)
cell2.imageButton.kf.setImage(with:url)
cell2.imageButton.isHidden = false
}
Issues in your tap function code. When you want to get a cell from the index path use the cellForRow method of table view. so instead of this
let cell = tblViewChat.dequeueReusableCell(withIdentifier: "senderCell") as! SenderTblCell
use this for both tap action.
let cell = tblViewChat.cellForRow(at: indexPath) as! SenderTblCell

If there is a like button next to each user, why could scrolling trigger random users' like buttons be highlighted?

I use self.like.alpha = 0.5 to grey out the like button next to the user who was liked. Scrolling causes the highlight to sometimes disappear and appear next to other users.
I've used self.like.alpha = 0.5 last various places in the code but it changes nothing.
#IBAction func likePressed(_ sender: Any) {
self.like.alpha = 0.5
let ref = Database.database().reference()
let keyToPost = ref.child("likes").childByAutoId().key
ref.child("humans").child(self.postID).observeSingleEvent(of: .value, with: {(snapshot) in
if let humans = snapshot.value as? [String: AnyObject] {
let updateLikes: [String: Any] = ["humansWhoLike/\(keyToPost)" : Auth.auth().currentUser!.uid]
ref.child("humans").child(self.postID).updateChildValues(updateLikes, withCompletionBlock: { (error, reff) in
if error == nil {
ref.child("humans").child(self.postID).observeSingleEvent(of: .value, with: { (snap) in
if let properties = snap.value as?[String: AnyObject]{
if let likes = properties["humansWhoLike"] as? [String : AnyObject] {
let count = likes.count
let update = ["likes" : count]
ref.child("humans").child(self.postID).updateChildValues(update)
}
}
})
}
})
}
})
ref.removeAllObservers()
}
What I need is for the like button that is clicked to be greyed out. It has to stay greyed out and the greying out should not jump to another user's like button.
/Updated code after 1st answer
public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
let like = cell.viewWithTag(3) as! UIButton
let immy = cell.viewWithTag(1) as! UIImageView
let person: Userx = humans[indexPath.row]
cell.lblName.text = person.Education
cell.postID = self.humans[indexPath.row].postID
if let PhotoPosts = person.PhotoPosts {
let url = URL(string: PhotoPosts)
immy.sd_setImage(with: url)
}
return cell
}
Remember that tableView cells are reusable. When you dequeue one, you cannot assume anything about the existing values. If you mark a cell liked (with button formatting), when that cell is reused, the formatting is still there.
When you dequeue a cell in your cellForRowAt function, you need to reset all the values according to your data store.
I am having a little trouble understanding your database design/usage, but based on the code you added to the post:
let currUser = Auth.auth().currentUser!.uid // better to add this as a VC level variable as you will do this lookup a lot.
let likeArray = person.humansWhoLike ?? []
let likeStatus = likeArray.contains(currentUser)
//from your code, 'like' is the button to be formatted
like.alpha = likeStatus ? 0.5 : 1.0

Failure while attempt to find specific cell in table view swift

I want to find specific cell class in my table view. I use following code:
func setConfirmEnabledIfNeed(){
let ip = IndexPath(row: 8, section: 0)
if let cell = tableView.cellForRow(at: ip) as? ConfirmBtnCell {
print("find confirm cell")
}
let c = tableView.cellForRow(at: ip) as? ConfirmBtnCell
print("type of cell \(type(of: c))")
}
print("find confirm cell") is never called, however, second print output: type of cell Optional<ConfirmBtnCell>, what is obviously what i need. But why first print not called?
My cell for row look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = viewModel.items[indexPath.row]
switch item {
case .selectable(let value, let placeholder, let type):
let selectionCell = tableView.dequeueReusableCell(withIdentifier: "SelectionCell") as! SelectionCell
if let placeholder = placeholder { selectionCell.setPlaceholder(placeholder) }
if let text = value as? String, !text.isEmpty { selectionCell.hidePlaceholder();
selectionCell.textLbl.text = text }
if let date = value as? Date { selectionCell.hidePlaceholder();
selectionCell.textLbl.text = DateFormatterUtil.getReadableStringFromDate(date) }
return selectionCell
case .back:
return tableView.dequeueReusableCell(withIdentifier: "BackBtnCell") as! BackBtnCell
case .confirm:
return tableView.dequeueReusableCell(withIdentifier: "ConfirmBtnCell") as! ConfirmBtnCell
case .editableNumbers(let text, let placeholder, let type):
let editableCell = tableView.dequeueReusableCell(withIdentifier: "TextEditCell") as! TextEditCell
editableCell.setup(isNumerical: true)
if let placeholder = placeholder { editableCell.setPlaceholder(placeholder) }
if let text = text {editableCell.hidePlaceholder(); editableCell.txtf.text = text }
editableCell.textChanged = {[weak self] text in
guard let text = text else { return }
if type == .sumCash {
self?.viewModel.collectedInfo[CorrectionChequeViewModel.infoKeys.sumCash.rawValue] = text
self?.setConfirmEnabledIfNeed()
}
if type == .sumElectronic {
self?.viewModel.collectedInfo[CorrectionChequeViewModel.infoKeys.sumElectronic.rawValue] = text
}
}
return editableCell
case .editableText(let text, let placeholder, let type):
let editableCell = tableView.dequeueReusableCell(withIdentifier: "TextEditCell") as! TextEditCell
editableCell.setup(isNumerical: false)
if let placeholder = placeholder { editableCell.setPlaceholder(placeholder) }
if let text = text {editableCell.hidePlaceholder(); editableCell.txtf.text = text }
editableCell.textChanged = {[weak self] text in
guard let text = text else { return }
if type == .sumCash {
self?.viewModel.collectedInfo[CorrectionChequeViewModel.infoKeys.description.rawValue] = text
}
if type == .sumElectronic {
self?.viewModel.collectedInfo[CorrectionChequeViewModel.infoKeys.number.rawValue] = text
}
}
return editableCell
default:
return UITableViewCell()
}
}
UPDATE:
I find that code work if it's not called from table view cellForRow method. I tried to launch that block of code with dispatch_after, and it works.
In your if statement, the value for tableView.cellForRow(at: ip) as? ConfirmBtnCell is nil, so you never enter the block.
In the second statement (let c = tableView.cellForRow(at: ip) as? ConfirmBtnCell), the value of c is an Optional<ConfirmBtnCell>. If you unwrap the optional, you will see that its unwrapped value is also a nil.
If your row is not visible, tableView.cellForRow will return a nil even when the cell is set. See the Apple Documentation.

Cell repopulating over each other in TableView

I'm currently working with a tableview that contains three sections. Each section loads it's data from an array that is loaded from a backend and is stored in a local array. The data downloads and displays without issue, but once I scroll, the cells reload themselves over their previous data. The reason I caught it, outside of the memory warning, was that the cell contains an image with a drop shadow and the shadow grows darker with each scroll. I have a feeling the issue is related to how I'm calling the cell at indexPath for each section but can't seem to find a solution. I've searched around and haven't been able to find a resolution yet.
The cells all share the same custom class and reload their respective data through the same outlets.
Not sure if this is relevant to the issue but the view is called from information in the AppDelegate.
Here's the code that I'm working with staring with number of rows in section.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return likesName.count
} else if section == 1 {
return commentsName.count
} else if section == 2 {
return bookmarksName.count
}
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("toggleCell", forIndexPath:indexPath) as! RightToggleCell
cell.userAvatar.layer.cornerRadius = cell.userAvatar.frame.size.width/2
cell.userAvatar.clipsToBounds = true
cell.artworkImage.contentMode = UIViewContentMode.ScaleAspectFill
cell.artworkImage.clipsToBounds = true
cell.artistName.text = likesName[indexPath.row]
cell.information.text = "liked your artwork"
likesImage[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.artworkImage.image = downloadedImage
}
}
likesAvatar[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.userAvatar.image = downloadedImage
}
}
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("toggleCell", forIndexPath:indexPath) as! RightToggleCell
cell.userAvatar.layer.cornerRadius = cell.userAvatar.frame.size.width/2
cell.userAvatar.clipsToBounds = true
cell.artworkImage.contentMode = UIViewContentMode.ScaleAspectFill
cell.artworkImage.clipsToBounds = true
cell.artistName.text = commentsName[indexPath.row]
cell.information.text = commentsInformation[indexPath.row]
commentsImage[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.artworkImage.image = downloadedImage
}
}
commentsAvatar[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.userAvatar.image = downloadedImage
}
}
} else if indexPath.section == 2 {
let cell = tableView.dequeueReusableCellWithIdentifier("toggleCell", forIndexPath:indexPath) as! RightToggleCell
cell.userAvatar.layer.cornerRadius = cell.userAvatar.frame.size.width/2
cell.userAvatar.clipsToBounds = true
cell.artworkImage.contentMode = UIViewContentMode.ScaleAspectFill
cell.artworkImage.clipsToBounds = true
cell.artistName.text = bookmarksName[indexPath.row]
cell.information.text = "bookmarked your artwork"
bookmarksImage[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.artworkImage.image = downloadedImage
}
}
bookmarksAvatar[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
cell.userAvatar.image = downloadedImage
}
}
}
return cell
}
It's a bit hard to line up your code, but it looks like you have:
let cell = ...
if ... {
let cell = ...
} else if ... {
let cell = ...
}
return cell
If this is what you have, the let cell inside each if is only scoped to exist within that if. Therefore, the return cell at the end will return the initial, empty cell. If this is the case, you should change it to:
if ... {
let cell = ...
return cell
} else if ... {
let cell = ...
return cell
} else {
let cell = ...
return cell
}
Note that the last condition must just be else and not else if or the compiler will tell you not all code paths return a value (when the last "if" fails). However, you're saying it's basically working, so something doesn't make sense.
Either way, cells get reused for new rows once a row moves off screen. Therefore, by the time your getDataInBackgroundWithBlock returns, the cell might belong to a different row. Given the drop-shadow is getting darker, it sounds like you have code in RightToggleCell that is rerunning when a cell gets reused.
When you load data in the background, you should store it outside the cell, and reload that row when the load finishes. Something (very roughly) along the lines of:
likesImage[indexPath.row].getDataInBackgroundWithBlock { (data, error) -> Void in
if let downloadedImage = UIImage(data: data!) {
artworkImages[indexPath.row] = downloadedImage
dispatch_async(dispatch_get_main_queue(), {
tableView.reloadRowsAtIndexPaths([indexPath.row], withRowAnimation: true)
})
}
}
The cellForRowAtIndexPath should display the relevant artworkImages if it's available, or use the above code if not. Note that any updating of the UI must be done on the main thread, which is why dispatch_async is being called above.
Instead of doing everything on the TableViewController i suggest you to put this code into your "RightToggleCell".
override func awakeFromNib() {
self.userAvatar.layer.cornerRadius = self.userAvatar.frame.size.width/2
self.userAvatar.clipsToBounds = true
self.artworkImage.contentMode = UIViewContentMode.ScaleAspectFill
self.artworkImage.clipsToBounds = true
}
By doing this iOS doesn't modify cornerRadius and clipsToBounds everytime you reload your TableView
With the guidance from Michael, I moved return cell inside of each if let statement and returned UITableViewCell() outside of the statements and that resolved the issue. Thanks again Michael, I up voted your response but I don't have a high enough reputation for it to post.

How do I fix this error"Could not cast value of type 'UIImageView' () to 'PFImageView' " to display images on query?

I'm trying to retrieve users profile pictures(File) from Parse onto my PFQueryTableViewController. The code I've written doesn't give me errors but every time I run my app crashes and says "Could not cast value of type UIImageView to PFImageView. I'm new to coding and don't know what that means. Is there something I have to fix in my code? I just want to retrieve the users image to display on my query.
Here is my code below for my FeedViewController:
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?, object: PFObject!) -> PFTableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("BubbleCell", forIndexPath: indexPath!) as! Bubbles
if let userPost : PFObject = self.posts.objectAtIndex(indexPath!.row) as! PFObject {
/ if let pic = object["photo"] as? PFFile {
// Make sure your imageView is a PFImageView
let imageView = cell.userImage as! PFImageView
// I assume this is the placeholder image while your load your image files from Parse
imageView.image = UIImage(named: "image.png")
imageView.file = pic
// Load the image file however you see fit
imageView.loadInBackground(nil)
}
Here is my code for my PostViewController:
#IBAction func postPressed(sender: AnyObject) {
let testObj = PFObject(className: "Test")
testObj["photo"] = PFUser.currentUser()?.valueForKey("photo") as! PFFile
testObj.saveInBackgroundWithBlock { (success:Bool, error :NSError?) -> Void in
if error == nil
{
print("***///detail is saved///***")
self.dismissViewControllerAnimated(true, completion: nil)
}
else {
self.alert()
}
}