首页 > 解决方案 > 输入新值时复制表的最后一行

问题描述

我的应用程序中有一个聊天功能。该页面将现有数据加载为名为 hhmessages 的数组。对话中的最后一条消息是“欢迎”,当我输入新文本“谢谢”并按 Enter 键时,表格会自动再次显示“欢迎”而不是“谢谢”。如果它退出页面并返回,它现在会显示“谢谢”作为最后一条消息。它在后端工作,只是即时更新在输入时没有显示 UITableView 中的值。

这适用于 iPhone 应用程序。更新以显示完整代码 - 现在反向已被删除,新条目显示为空白。

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
    }

}

标签: arraysswiftuitableview

解决方案


问题是你在插入新文本

let indexPath = IndexPath(row: hhmessages.count - 1, section: 0)

这是数组的最后一个字段。但在你的 cellForRow: 你有

let text = hhmessages.reversed()[indexPath.row]["messagetext"] as! String

它在 tableView 的最后一个单元格中显示了数组的第一个字段。

我认为那是你的问题。


推荐阅读