输入新值时复制表的最后一行 [英] Duplicating last row of table when new value entered
问题描述
我的应用程序中具有聊天功能。该页面将现有数据作为名为hhmessages的数组加载。对话中的最后一条消息是欢迎,当我输入新文本谢谢并按Enter时,表格会自动再次显示欢迎而不是谢谢。如果它退出页面并立即返回显示谢谢作为最后一条消息。它在后端工作,只是当输入时即时更新未在UITableView中显示值。
I have a chat feature within my app. The page loads the existing data as an array called hhmessages. The last message in the conversation was 'Welcome', when I enter new text 'Thank You' and hit enter the table automatically displays 'Welcome' again instead of 'Thank You.' If it exit out of the page and come back it now shows 'Thank You' as the last message. It's working on the backend just the instant update isn't showing the value in the UITableView when entered.
这是针对iPhone应用程序的。
已更新为显示完整代码-现在已删除反向条目,新条目显示为空白。
This is for an iPhone app. UPDATED TO SHOW COMPLETE CODE - Now that reverse has been removed the new entry shows as blank.
import UIKit
import Foundation
extension String {
// Calculeta the hight string Function
func calculateTextFrameRect(
objectsInPlaceHeight: CGFloat,
objectsInPlaceWidth: CGFloat,
fontSize: CGFloat,
fontWeight: CGFloat) -> CGSize
{
let bounding = CGSize(width: UIScreen.main.bounds.width - objectsInPlaceWidth, height: .infinity)
let rect = NSString(string: self).boundingRect(
with: bounding,
options: NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin),
attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(rawValue: fontWeight))],
context: nil)
return CGSize(width: UIScreen.main.bounds.width, height: rect.height + objectsInPlaceHeight )
}
}
// Messages for test
let frame = CGRect(origin: .zero, size: CGSize.init(width: 375, height: 559))
class Message {
let message: String
var incoming: [Int]
let image: UIImage
var avas = UIImage()
init(message: String, image: UIImage, incoming: Int, avas: UIImage) {
self.message = message
self.image = image
self.incoming = [incoming]
self.avas = avas
}
}
class ConversationViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {
//var user = NSDictionary()
var messages = NSDictionary()
//var guest = NSDictionary()
@IBOutlet var senderLbl: UILabel!
@IBOutlet var senderageLbl: UILabel!
@IBOutlet var senderraceLbl: UILabel!
@IBOutlet var sendergenderLbl: UILabel!
@IBOutlet var sendercityLbl: UILabel!
@IBOutlet var senderstateLbl: UILabel!
// @IBOutlet var tableView: UITableView!
var hhmessages = [AnyObject]()
//var messages: [Message] = []
var pictures = [UIImage]()
var avas = [UIImage]()
var avaURL = [String]()
var isLoading = false
var skip = 0
var limit = 50
var images = [UIImage]()
var incoming: [Int] = []
var comments = [String]()
var ids = [String]()
//var isSent: String = ""
var isPictureSelected = false
//var currentUser_ava = [Any]()
@IBOutlet var pictureImg: UIImageView!
@IBOutlet var avaImg: UIImageView!
@IBOutlet var viewprofile_btn: UIButton!
@IBOutlet var imgprofile_btn: UIButton!
@IBOutlet var replyTxt: UITextView!
//var replyTxt:UITextView!
@IBOutlet var replyTxt_height: NSLayoutConstraint!
@IBOutlet var replyTxt_bottom: NSLayoutConstraint!
@IBOutlet var picSelect: UIButton!
@IBOutlet var replyBtn: UIButton!
var puuid = String()
var imageSelected = false
var coolIndicator: UIActivityIndicatorView!
var commentsTextView_bottom_identity = CGFloat()
@IBOutlet var tableView: UITableView!
// Table View here + basic configuration
override func viewDidLoad() {
//self.tabBarController?.tabBar.isHidden = true
super.viewDidLoad()
tableView.transform = CGAffineTransform(rotationAngle: -(CGFloat)(Double.pi));
// dynamic cell height
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 200
loadPosts()
replyTxt.layer.cornerRadius = replyTxt.bounds.width / 50
replyTxt.backgroundColor = UIColor.clear
replyTxt.layer.borderColor = UIColor.gray.cgColor
replyTxt.layer.borderWidth = 1.0
let username = messages["sender"] as? String
self.navigationItem.title = username
}
// pre last func
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// remove observers of notification when the viewController is left
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
// exec-d once notification is caught -> KeyboardWillShow
@objc func keyboardWillShow(_ notification: Notification) {
// getting the size of the keyboard
if let keyboard_size = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
// increasing the bottom constraint by the keyboard's height
replyTxt_bottom.constant += keyboard_size.height
}
// updating the layout with animation
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
// exec-d once notification is caught -> KeyboardWillHide
@objc func keyboardWillHide() {
// updating the layout with animation
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//replyTxt().resignFirstResponder()
self.view.endEditing(false)
}
// TABLEVIEW
// Number os cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return hhmessages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let colorSmoothGray = UIColor(red: 229/255, green: 229/255, blue: 234/255, alpha: 1)
let colorBrandBlue = UIColor(red: 148 / 255, green: 33 / 255, blue: 147 / 255, alpha: 1)
let pictureURL = hhmessages[indexPath.row]["uploadpath"] as? String
print("test 1", pictureURL)
// no picture in the post
if pictureURL == nil || pictureURL == "" {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ConversationCell
cell.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
// shortcuts
let hhpost = hhmessages[indexPath.row]
let text = hhpost["messagetext"] as? String
cell.messageLbl.text = text
cell.messageLbl.textAlignment = .right
cell.messageLbl.backgroundColor = colorSmoothGray
cell.messageLbl.textColor = .black
cell.messageLbl.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
cell.messageLbl.font?.withSize(25)
cell.messageLbl.clipsToBounds = true
cell.messageLbl.sizeToFit()
pictures.append(UIImage())
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PicCell", for: indexPath) as! PicConversationCell
cell.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
let smimages = hhmessages[indexPath.row]["path"] as? UIImage
cell.smavaImg.image = smimages
for i in 0 ..< self.incoming.count {
// Confiture the constraints for cell
if self.incoming[indexPath.row] == 1 {
// Text
cell.messageLbl.textAlignment = .left
cell.messageLbl.backgroundColor = colorBrandBlue
cell.messageLbl.textColor = .white
cell.messageLbl.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
cell.messageLbl.font?.withSize(25)
cell.messageLbl.clipsToBounds = true
// Constraints
cell.lefBubbleConstraint.isActive = true
cell.rightBubbleConstraint.isActive = false
if cell.postpictureImg.image == nil {
cell.postpictureImg.backgroundColor = .black
cell.postpictureImg.clipsToBounds = true
}
else {
cell.postpictureImg.backgroundColor = .black
cell.postpictureImg.clipsToBounds = true
}
}
else if self.incoming[indexPath.row] == 0 {
// Text
cell.messageLbl.textAlignment = .right
cell.messageLbl.backgroundColor = colorSmoothGray
cell.messageLbl.textColor = .black
cell.messageLbl.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)
cell.messageLbl.font?.withSize(25)
cell.messageLbl.clipsToBounds = true
// Constraints
cell.lefBubbleConstraint.isActive = false
cell.rightBubbleConstraint.isActive = true
if cell.postpictureImg.image == nil {
cell.postpictureImg.backgroundColor = .black
cell.postpictureImg.clipsToBounds = true
}
else {
cell.postpictureImg.backgroundColor = .black
cell.postpictureImg.clipsToBounds = true
}
}
if cell.lefBubbleConstraint.isActive == true {
cell.lefImageConstraint.isActive = true
cell.rightImageConstraint.isActive = false
} else {
cell.lefImageConstraint.isActive = false
cell.rightImageConstraint.isActive = true
}
let pictureString = hhmessages[indexPath.row]["uploadpath"] as! String
let pictureURL = URL(string: pictureString)!
// if there are still pictures to be loaded
if hhmessages.count != pictures.count {
URLSession(configuration: .default).dataTask(with: pictureURL) { (data, response, error) in
// downloaded
if let image = UIImage(data: data!) {
self.pictures.append(image)
DispatchQueue.main.async {
cell.postpictureImg.image = image
}
}
}.resume()
// cached picture
} else {
DispatchQueue.main.async {
cell.postpictureImg.image = self.pictures[indexPath.row]
}
}
}
return cell
}
}
// pre load func
override func viewDidAppear(_ animated: Bool) {
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
// func of loading posts from server
@objc func loadPosts() {
//isLoading = true
let me = user!["username"] as! String
let meid = user!["id"] as! String
print(meid)
print(me)
//print(username)
let uuid = messages["uuid"] as! String
print(uuid)
// accessing php file via url path
let url = URL(string: "http://localhost/message.php")!
// pass information to php file
let body = "username=\(me)&uuid=\(uuid)&recipient_id=\(meid)"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = body.data(using: String.Encoding.utf8)
tableView.transform = CGAffineTransform(rotationAngle: -(CGFloat)(Double.pi));
// launch session
URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
// no error of accessing php file
// error occured
if error != nil {
Helper().showAlert(title: "Server Error", message: error!.localizedDescription, in: self)
//self.isLoading = false
return
}
do {
// access data - safe mode
guard let data = data else {
Helper().showAlert(title: "Data Error", message: error!.localizedDescription, in: self)
//self.isLoading = false
return
}
// getting content of $returnArray variable of php file
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
// accessing json data - safe mode
guard let posts = json?["messages"] as? [NSDictionary] else {
//self.isLoading = false
return
}
// assigning all successfully loaded posts to our Class Var - posts (after it got loaded successfully)
self.hhmessages = posts
self.tableView.reloadData()
// scroll to the latest index (latest cell -> bottom)
let indexPath = IndexPath(row: self.hhmessages.count - 1, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
// self.isLoading = false
} catch {
Helper().showAlert(title: "JSON Error", message: error.localizedDescription, in: self)
//self.isLoading = false
return
}
}
}.resume()
}
@IBAction func viewprofile_clicked(_ sender: Any) {
// performSegue(withIdentifier: "guest2", sender: self.guest)
}
@IBAction func imgprofile_clicked(_ sender: Any) {
// performSegue(withIdentifier: "guest2", sender: self.guest)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "guest" {
if let destination = segue.destination as? GuestViewController {
destination.guest = messages
}
}
}
// function sending requset to PHP to uplaod a file
func uploadPost() {
// validating vars before sending to the server
guard let user_id = user?["id"] as? String, let username = user?["username"] as? String, let avaPath = user?["ava"] else {
// converting url string to the valid URL
if let url = URL(string: user?["ava"] as! String) {
// downloading all data from the URL
guard let data = try? Data(contentsOf: url) else {
return
}
// converting donwloaded data to the image
guard let image = UIImage(data: data) else {
return
}
// assigning image to the global var
let currentUser_ava = image
}
return
}
let user_id_int = Int(user_id)!
let messagetext = replyTxt.text.trimmingCharacters(in: .whitespacesAndNewlines)
hhmessages.insert(messagetext as AnyObject, at: hhmessages.endIndex)
let indexPath = IndexPath(row: hhmessages.count - 1, section: 0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
tableView.transform = CGAffineTransform(rotationAngle: -(CGFloat)(Double.pi));
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
replyTxt.text = ""
textViewDidChange(replyTxt)
let recipient = messages["username"] as! String
let rid = String(describing: messages["recipient_id"]!)
let uuid = messages["uuid"] as! String
puuid = UUID().uuidString
// prepare request
let url = URL(string: "http://localhost/messagepost.php")!
let body = "sender_id=\(user_id)&sender=\(username)&text=\(messagetext)&recipient_id=\(rid)&recipient=\(recipient)&uuid=\(uuid)&puuid=\(puuid)"
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = body.data(using: .utf8)
// send request
URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
// error happened
if error != nil {
Helper().showAlert(title: "Server Error", message: error!.localizedDescription, in: self)
return
}
do {
// converting received data from the server into json format
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary
// safe mode of casting json
guard let parsedJSON = json else {
return
}
// if the status of JSON is 200 - success
if parsedJSON["status"] as! String == "200" {
} else {
Helper().showAlert(title: "400", message: parsedJSON["status"] as! String, in: self)
return
}
// json error
} catch {
Helper().showAlert(title: "JSON Error", message: error.localizedDescription, in: self)
return
}
}
}.resume()
}
// exec-d whenever delegated textView has been changed by chars
func textViewDidChange(_ textView: UITextView) {
// declaring new size of the textView. we increase the height
let new_size = textView.sizeThatFits(CGSize.init(width: textView.frame.width, height: CGFloat(MAXFLOAT)))
// assign new size to the textView
textView.frame.size = CGSize.init(width: CGFloat(fmaxf(Float(new_size.width), Float(textView.frame.width))), height: new_size.height)
//UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
//}
}
@IBAction func picSelect_clicked(_ sender: AnyObject) {
// calling picker for selecting iamge
showActionSheet()
}
// this function launches Action Sheet for the photos
func showActionSheet() {
// declaring action sheet
let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// declaring camera button
let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
// if camera available on device, than show
if UIImagePickerController.isSourceTypeAvailable(.camera) {
self.showPicker(with: .camera)
}
}
// declaring library button
let library = UIAlertAction(title: "Photo Library", style: .default) { (action) in
// checking availability of photo library
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
self.showPicker(with: .photoLibrary)
}
}
// declaring cancel button
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
// adding buttons to the sheet
sheet.addAction(camera)
sheet.addAction(library)
sheet.addAction(cancel)
// present action sheet to the user finally
self.present(sheet, animated: true, completion: nil)
}
// takes us to the PickerController (Controller that allows us to select picture)
func showPicker(with source: UIImagePickerControllerSourceType) {
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
picker.sourceType = source
present(picker, animated: true, completion: nil)
}
// executed whenever the image has been picked via pickerController
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// accessing selected image
let image = info[UIImagePickerControllerEditedImage] as? UIImage
// assigning selected image to pictureImageView
pictureImg.image = image
// cast boolean as TRUE -> Picture Is Selected
isPictureSelected = true
// remove pickerController
pictureImg.image = info[UIImagePickerControllerEditedImage] as? UIImage
self.dismiss(animated: true, completion: nil)
// cast as a true to save image file in server
if pictureImg.image == info[UIImagePickerControllerEditedImage] as? UIImage {
imageSelected = true
}
dismiss(animated: true, completion: nil)
}
// exec when pictureImageView has been tapped
@IBAction func pictureImageView_tapped(_ sender: Any) {
// declaring action sheet
let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
// declaring delete button
let delete = UIAlertAction(title: "Delete", style: .destructive) { (action) in
self.pictureImg.image = UIImage()
}
// declaring cancel button
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
// adding buttons to the sheet
sheet.addAction(delete)
sheet.addAction(cancel)
// present action sheet to the user finally
self.present(sheet, animated: true, completion: nil)
}
// custom body of HTTP request to upload image file
func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, imageDataKey: Data, boundary: String) -> Data {
let body = NSMutableData();
if parameters != nil {
for (key, value) in parameters! {
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(value)\r\n")
}
}
// if file is not selected, it will not upload a file to server, because we did not declare a name file
var filename = ""
if imageSelected == true {
filename = "notes-\(puuid).jpg"
}
let mimetype = "image/jpg"
body.appendString("--\(boundary)\r\n")
body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimetype)\r\n\r\n")
body.append(imageDataKey)
body.appendString("\r\n")
body.appendString("--\(boundary)--\r\n")
return body as Data
}
@IBAction func replyBtn_clicked(_ sender: Any) {
if replyTxt.text.isEmpty == false && replyTxt.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false {
uploadPost()
//tableView.reloadData()
}
}
@objc func keyboardWillChange(notification: NSNotification) {
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = targetFrame.origin.y - curFrame.origin.y
//print("deltaY",deltaY)
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
self.replyTxt.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
self.tableView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
self.replyBtn.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
self.picSelect.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
},completion: nil)
}
func textFieldShouldReturn(_ replyTxt: UITextField) -> Bool {
replyTxt.resignFirstResponder()
return true
}
}
推荐答案
问题是您要在
let indexPath = IndexPath(行:hhmessages.count-1,区域:0)
这是数组的最后一个字段。
但在您的cellForRow中:您有
which is the last field of the array. but in your cellForRow: you have
let text = hhmessages.reversed()[indexPath.row] [ messagetext]如!字符串
它在tableView的最后一个单元格中显示数组的第一个字段。
which shows the first field of your array in the last cell of tableView.
我认为那是您的问题。
I think thats your problem.
这篇关于输入新值时复制表的最后一行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!