Instagram / IGListKit

A data-driven UICollectionView framework for building fast and flexible lists.
https://instagram.github.io/IGListKit/
MIT License
12.88k stars 1.54k forks source link

Proper Implementation #949

Closed Smiller193 closed 7 years ago

Smiller193 commented 7 years ago

[So I have been attempting to implement IGListKit For a couple days now I believe my implementation is correct. I'm using it currently to load comments when a user clicks on a post. I'm not 100% sure if I have implemented it right so this is less of an issue and more of a verification in the hopes that my code has no issue.

This is my comments controller

import UIKit
import IGListKit
import Firebase

 class NewCommentsViewController: UIViewController, UITextFieldDelegate {
//array of comments which will be loaded by a service function
var comments = [CommentGrabbed]()
var messagesRef: DatabaseReference?
var bottomConstraint: NSLayoutConstraint?
public var eventKey = ""
//This creates a lazily-initialized variable for the IGListAdapter. The initializer requires three parameters:
//1 updater is an object conforming to IGListUpdatingDelegate, which handles row and section updates. IGListAdapterUpdater is a default implementation that is suitable for your usage.
//2 viewController is a UIViewController that houses the adapter. This view controller is later used for navigating to other view controllers.
//3 workingRangeSize is the size of the working range, which allows you to prepare content for sections just outside of the visible frame.](url)

lazy var adapter: ListAdapter = {
    return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
}()

// 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
let collectionView: UICollectionView = {
    // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
    let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
    // 3 The background color is set to white
    view.backgroundColor = UIColor.white
    return view
}()

//will fetch the comments from the database and append them to an array
fileprivate func fetchComments(){
    messagesRef = Database.database().reference().child("Comments").child(eventKey)
    print(eventKey)
    print(comments.count)
    messagesRef?.observe(.childAdded, with: { (snapshot) in
        print(snapshot)
        guard let commentDictionary = snapshot.value as? [String: Any] else{
            return
        }
        print(commentDictionary)
        guard let uid = commentDictionary["uid"] as? String else{
            return
        }
        UserService.show(forUID: uid, completion: { (user) in
            if let user = user {
                var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                commentFetched.commentID = snapshot.key
                let filteredArr = self.comments.filter { (comment) -> Bool in
                    return comment.commentID == commentFetched.commentID
                }
                if filteredArr.count == 0 {
                    self.comments.append(commentFetched)
                }
                print(self.comments)
                self.adapter.performUpdates(animated: true)
            }
            self.comments.sort(by: { (comment1, comment2) -> Bool in
                return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
            })
            self.comments.forEach({ (comments) in
            })
        })
    }, withCancel: { (error) in
        print("Failed to observe comments")
    })

    //first lets fetch comments for current event
}

lazy var submitButton : UIButton = {
    let submitButton = UIButton(type: .system)
    submitButton.setTitle("Submit", for: .normal)
    submitButton.setTitleColor(.black, for: .normal)
    submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
    submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside)
    submitButton.isEnabled = false
    return submitButton
}()

//allows you to gain access to the input accessory view that each view controller has for inputting text
lazy var containerView: UIView = {
    let containerView = UIView()
    containerView.backgroundColor = .white
    containerView.addSubview(self.submitButton)
    self.submitButton.anchor(top: containerView.topAnchor, left: nil, bottom: containerView.bottomAnchor, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0)

    containerView.addSubview(self.commentTextField)
    self.commentTextField.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: containerView.bottomAnchor, right: self.submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 180, width: 0, height: 0)
    self.commentTextField.delegate = self
    let lineSeparatorView = UIView()
    lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
    containerView.addSubview(lineSeparatorView)
    lineSeparatorView.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: nil, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)

    return containerView
}()

lazy var commentTextField: UITextField = {
    let textField = UITextField()
    textField.placeholder = "Add a comment"
    textField.delegate = self
    textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
    return textField
}()

func textFieldDidChange(_ textField: UITextField) {
    let isCommentValid = commentTextField.text?.characters.count ?? 0 > 0
    if isCommentValid {
        submitButton.isEnabled = true
    }else{
        submitButton.isEnabled = false
    }
}

func handleSubmit(){
    guard let comment = commentTextField.text, comment.characters.count > 0 else{
        return
    }
    let userText = Comments(content: comment, uid: User.current.uid, profilePic: User.current.profilePic!)
    sendMessage(userText)
    // will remove text after entered
    self.commentTextField.text = nil
}

func flagButtonTapped (from cell: CommentCell){
    guard let indexPath = collectionView.indexPath(for: cell) else { return }

    // 2
    let comment = comments[indexPath.item]
    _ = comment.uid

    // 3
    let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

    // 4
    if comment.uid != User.current.uid {
        let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
            ChatService.flag(comment)

            let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
            okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
            self.present(okAlert, animated: true)
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        alertController.addAction(cancelAction)
        alertController.addAction(flagAction)
    }else{
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
            ChatService.deleteComment(comment, self.eventKey)
            let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
            okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
            self.present(okAlert, animated: true)
            self.adapter.performUpdates(animated: true)

        })
        alertController.addAction(cancelAction)
        alertController.addAction(deleteAction)

    }
    present(alertController, animated: true, completion: nil)

}

func handleKeyboardNotification(notification: NSNotification){
    if let userinfo = notification.userInfo{

        let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        self.bottomConstraint?.constant = -(keyboardFrame.height)

        let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
        self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0

        UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
            self.view.layoutIfNeeded()
        }, completion: { (completion) in
            if self.comments.count > 0  && isKeyboardShowing{
                let indexPath = NSIndexPath(item: self.comments.count-1, section: 0)
                self.collectionView.scrollToItem(at: indexPath as IndexPath, at: .top, animated: true)
            }
        })
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(collectionView)
    collectionView.addSubview(containerView)
    collectionView.alwaysBounceVertical = true
    view.addConstraintsWithFormatt("H:|[v0]|", views: containerView)
    view.addConstraintsWithFormatt("V:[v0(48)]", views: containerView)
    bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
    view.addConstraint(bottomConstraint!)
    adapter.collectionView = collectionView
    adapter.dataSource = self
    NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "CommentCell")
    fetchComments()
    // Do any additional setup after loading the view.
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    tabBarController?.tabBar.isHidden = true
    submitButton.isUserInteractionEnabled = true

}
//viewDidLayoutSubviews() is overridden, setting the collectionView frame to match the view bounds.
override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    collectionView.frame = view.bounds
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

}

 extension NewCommentsViewController: ListAdapterDataSource {
// 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries.
func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
    print("comments = \(comments)")
    return comments
}

// 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller.
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
    //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
    return CommentsSectionController()
}

// 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature.
func emptyView(for listAdapter: ListAdapter) -> UIView? {
    let view = UIView()
    view.backgroundColor = UIColor.white
    return view
}

}

extension NewCommentsViewController { func sendMessage(_ message: Comments) { ChatService.sendMessage(message, eventKey: eventKey)

}

}

This is my section controller

 import UIKit
 import IGListKit
import Foundation

 class CommentsSectionController: ListSectionController {
 var comment: CommentGrabbed?
     override init() {
         super.init()
         inset = UIEdgeInsets(top: 0, left: 0, bottom: 15, right: 0)
     }
     // MARK: IGListSectionController Overrides
     override func numberOfItems() -> Int {
         return 1
     } 

override func sizeForItem(at index: Int) -> CGSize {
    let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
    let dummyCell = CommentCell(frame: frame)
     dummyCell.comment = comment
    dummyCell.layoutIfNeeded()
    let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
    let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
    let height = max(40+8+8, estimatedSize.height)
    return  CGSize(width: collectionContext!.containerSize.width, height: height)

}

override var minimumLineSpacing: CGFloat {
    get {
        return 0.0
    }
    set {
        self.minimumLineSpacing = 0.0
    }
}

override func cellForItem(at index: Int) -> UICollectionViewCell {
    guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else {
        fatalError()
    }
    print(comment)

    cell.comment = comment
    return cell
}
override func didUpdate(to object: Any) {
    comment = object as! CommentGrabbed
}
override func didSelectItem(at index: Int){

}

} ](url) Thanks in advance

](url)

weyert commented 7 years ago

The GitHawk is a nice source of how you can leverage IGListKit in your applications. But at first sight it looks correct

Sent with GitHawk

rnystrom commented 7 years ago

@Smiller193 what isn’t working exactly?

Sent with GitHawk

weyert commented 7 years ago

@rnystrom I think it's more a question whether this is a 'best practice'-implementation

Smiller193 commented 7 years ago

Yeah, it is more of a best practice question. However, I do have two issues that I want to bring to light So each one of my section controllers is, in essence, a collection view cell. In my previous implementation, I was able to interact with those cells via a Button that is present in each cell. The button is still there but It no longer responds to the tap gesture I have setup to it.

Secondly, how would I add my header back? The header contained the title text "Comment" and a button that lets you dismiss the controller.

weyert commented 7 years ago

If you want to reintroduce your headers, you need to implement ListSupplementaryViewSource in your SectionController so you add the supplementary view (here a header) back. An example of this is the FeedItemSectionController in the Example-code.

Regarding getting the button working again, you can have a look at the guide https://github.com/Instagram/IGListKit/blob/master/Guides/Modeling%20and%20Binding.md which shows an example adding a like button.

Sent with GitHawk

Smiller193 commented 7 years ago

Thank you, I will take a look at it

Smiller193 commented 7 years ago

So I implemented the header similar to how it is implemented in the FeedItemSectionController however it seems to be adding a header for each and every comment cell. When in all reality I only need one. Below is my implementation. @weyert

func supportedElementKinds() -> [String] {
    return [UICollectionElementKindSectionHeader]
}
func viewForSupplementaryElement(ofKind elementKind: String, at index: Int) -> UICollectionReusableView {
    guard let view = collectionContext?.dequeueReusableSupplementaryView(ofKind: elementKind, for: self, class: CommentHeader.self, at: index) as? CommentHeader else{
        fatalError()
    }
    view.handle = "Comments"
    return view
}
func sizeForSupplementaryView(ofKind elementKind: String, at index: Int) -> CGSize {
    return CGSize(width: collectionContext!.containerSize.width, height: 40)
}
Smiller193 commented 7 years ago

It seems as though the header is attached to the cell and not the view

weyert commented 7 years ago

Yes, it's adding a section to every instance of the section controller. Check out the SupplementaryViewController in the Example. I think that would be easiest way to resolve this issue :)

Sent with GitHawk

Smiller193 commented 7 years ago

Thanks I will take a look into that and keep you updated

Smiller193 commented 7 years ago

Um I don't get it

/** Copyright (c) 2016-present, Facebook, Inc. All rights reserved.

The examples provided by Facebook are for non-commercial testing and evaluation purposes only. Facebook reserves all rights not expressly granted.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */

import UIKit import IGListKit

final class SupplementaryViewController: UIViewController, ListAdapterDataSource {

lazy var adapter: ListAdapter = {
    return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
}()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

let feedItems = [
    FeedItem(pk: 1, user: User(pk: 100, name: "Jesse", handle: "jesse_squires"), comments: ["You rock!", "Hmm you sure about that?"]),
    FeedItem(pk: 2, user: User(pk: 101, name: "Ryan", handle: "_ryannystrom"), comments: ["lgtm", "lol", "Let's try it!"]),
    FeedItem(pk: 3, user: User(pk: 102, name: "Ann", handle: "abaum"), comments: ["Good luck!"]),
    FeedItem(pk: 4, user: User(pk: 103, name: "Phil", handle: "phil"), comments: ["yoooooooo", "What's the eta?"])
]

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(collectionView)
    adapter.collectionView = collectionView
    adapter.dataSource = self
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    collectionView.frame = view.bounds
}

// MARK: ListAdapterDataSource

func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
    return feedItems
}

func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
    return FeedItemSectionController()
}

func emptyView(for listAdapter: ListAdapter) -> UIView? { return nil }

} There seems to be nothing here about rendering headers

rnystrom commented 7 years ago

@Smiller193 check inside FeedItemSectionController

Sent with GitHawk

Smiller193 commented 7 years ago

@rnystrom the implementation I followed in there added a header to each and every cell. Is there something there that I missed?

Smiller193 commented 7 years ago
screen shot 2017-09-25 at 8 46 11 pm
Smiller193 commented 7 years ago

Sorry for all the questions

rnystrom commented 7 years ago

It sounds like you're trying to get a single header for everything? If that's the case, then I'd recommend using a separate section controller to represent the header. It'll be a UICollectionViewCell in the end, but that's not really important (unless you need sticky headers).

Another option is to gather all of your data into a single section controller that has a supplementary view header.

I'd probably lean towards a separate section controller as the header. We do this a lot in Instagram and it works really, really well for us.

Sorry for all the questions

Np! That's what our issues section is for 😄

Smiller193 commented 7 years ago

Thank you I should be able to do that easily with the knowledge gained from conversing with you guys

Smiller193 commented 7 years ago

Sorry is there an example that just creates the header without attaching some sort of object or ListDiffable Model to it.

// // CommentsHeaderSectionController.swift // Eventful // // Created by Shawn Miller on 9/26/17. // Copyright © 2017 Make School. All rights reserved. //

import UIKit import IGListKit

class CommentsHeaderSectionController: ListSectionController { override init(){ super.init() } // MARK: IGListSectionController Overrides override func numberOfItems() -> Int { return 1 }

override func sizeForItem(at index: Int) -> CGSize {
    let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
    let dummyCell = CommentHeader(frame: frame)
    dummyCell.layoutIfNeeded()
    let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
    let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
    let height = max(40+8+8, estimatedSize.height)
    return  CGSize(width: collectionContext!.containerSize.width, height: height)

}

override func cellForItem(at index: Int) -> UICollectionViewCell {
    guard let cell = collectionContext?.dequeueReusableCell(of: CommentHeader.self, for: self, at: index) as? CommentHeader else {
        fatalError()
    }
    cell.handle = "Comments"
    return cell
}

override func didUpdate(to object: Any) {

}
override func didSelectItem(at index: Int){
}

}

This what I have but when I go to my main controller and try to add it in the objects(for: ) method I can't because the thing to add is not ListDiffable or there is not a listDiffable model for it to seemingly relate to.

I got the button thing figured out though :)

Smiller193 commented 7 years ago

Would I just do a check for the object and then return the sectionController in this method

  func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> 
ListSectionController {
        //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
        return CommentsSectionController()
   }
Smiller193 commented 7 years ago

@rnystrom

weyert commented 7 years ago

In my project I was using a private let addStore = "addStore" as ListDiffable and then:

func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
    return [addStore] + feedItems
}
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> 
ListSectionController {
       if let object = object as? ListDiffable, object === addStore {
            return AddStoreSectionController(delegate: self)
        }
        return CommentsSectionController()
   }
Smiller193 commented 7 years ago

Thanks that worked perfectly. Now I just have to go back and make the button work again @weyert

Smiller193 commented 7 years ago

Sorry but can I maybe ask one more thing

weyert commented 7 years ago

Of course :)

Sent with GitHawk

weyert commented 7 years ago

What's your question? 😀

Sent with GitHawk

Smiller193 commented 7 years ago

So I understand how to make use of protocols to communicate that a button was pressed in another class. Now I have this function in my NewCommentsViewController that is supposed to handle all things related to that comment; both flagging and deleteing.

This function is listed below

   func flagButtonTapped (from cell: CommentCell){
        guard let indexPath = self.collectionView.indexPath(for: cell) else { return }

        // 2
        let comment = comments[indexPath.item]
        _ = comment.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment, self.eventKey)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
                self.adapter.performUpdates(animated: true)

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        present(alertController, animated: true, completion: nil)

    }

I will also include my comment cell class for reference.

import Foundation
import UIKit

protocol CommentCellDelegate: class {
    func optionsButtonTapped(cell: CommentCell)
}
class CommentCell: UICollectionViewCell {
    weak var delegate: CommentCellDelegate? = nil

    override var reuseIdentifier : String {
        get {
            return "cellID"
        }
        set {
            // nothing, because only red is allowed
        }
    }
    var didTapOptionsButtonForCell: ((CommentCell) -> Void)?

    var comment: CommentGrabbed?{
        didSet{
            guard let comment = comment else{
                return
            }

            // textLabel.text = comment.content
            //shawn was also here
            profileImageView.loadImage(urlString: comment.user.profilePic!)
            //  print(comment.user.username)
            let attributedText = NSMutableAttributedString(string: comment.user.username!, attributes: [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 14)])

            attributedText.append(NSAttributedString(string: " " + (comment.content), attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14)]))
            textView.attributedText = attributedText

        }
    }

    let textView: UITextView = {
        let textView = UITextView()
        textView.font = UIFont.systemFont(ofSize: 14)
        textView.isScrollEnabled = false
        textView.isEditable = false
        // label.numberOfLines = 0
        //label.backgroundColor = UIColor.lightGray
        return textView
    }()

    let profileImageView: CustomImageView = {
        let iv = CustomImageView()
        iv.clipsToBounds = true
        iv.contentMode = .scaleAspectFill
        return iv
    }()

    lazy var flagButton: UIButton = {
        let flagButton = UIButton(type: .system)
        flagButton.setImage(#imageLiteral(resourceName: "icons8-Info-64"), for: .normal)
        flagButton.addTarget(self, action: #selector(optionsButtonTapped), for: .touchUpInside)
        return flagButton
    }()

    func optionsButtonTapped (){
        didTapOptionsButtonForCell?(self)
    }

    func onOptionsTapped() {
        delegate?.optionsButtonTapped(cell: self)
    }

    override init(frame: CGRect){
        super.init(frame: frame)
        // backgroundColor = .yellow
        addSubview(textView)
        addSubview(profileImageView)
        addSubview(flagButton)
        textView.anchor(top: topAnchor, left: profileImageView.rightAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 4, paddingBottom: 4, paddingRight: 4, width: 0, height: 0)
        profileImageView.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 8, paddingLeft: 8, paddingBottom: 0, paddingRight: 0, width: 40, height: 40)
        profileImageView.layer.cornerRadius = 40/2
        flagButton.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: 4, paddingLeft: 0, paddingBottom: 0, paddingRight: 4, width: 40, height: 40)
        flagButton.addTarget(self, action: #selector(CommentCell.onOptionsTapped), for: .touchUpInside)

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

So my question is how would I call the first function from this class and still present the alert controller the same way? An additional question stems from the removal of the comment when the deletion task is done. Will the cell auto delete itself? Before I decided to implement IGListKit the only way the cell would disappear was if I left and came back I want it to seemingly delete itself in real time.

Smiller193 commented 7 years ago

@rnystrom @weyert

weyert commented 7 years ago

I normally handle it all in the section controller but you can handle it in your VC if you want by just passing the delegate in the section controller e.g. as shown in GitHawk repo: https://github.com/rnystrom/GitHawk/blob/f8d0cc7f12c6adebfff723ea6029feffa08ee9ac/Classes/Issues/IssuesViewController.swift#L410

If you want to handle it in the section controller you should be able to access the view controller instance in the section controller. See: https://github.com/Instagram/IGListKit/blob/master/Source/IGListSectionController.h#L119

Smiller193 commented 7 years ago

So when I delete my comment it does not disappear from the section controller for some odd reason like it is gone from my database but my controller does not recognize this how would I simulate a real time deletion using this?

Smiller193 commented 7 years ago

@weyert @rnystrom

weyert commented 7 years ago

Did you make sure the data source got updated and you call the update on the IGListKit's adapter?

Sent with GitHawk

Smiller193 commented 7 years ago

Um am I able to call that via the section controller?

Smiller193 commented 7 years ago

@weyert

Smiller193 commented 7 years ago
 //will fetch the comments from the database and append them to an array
    fileprivate func fetchComments(){
        messagesRef = Database.database().reference().child("Comments").child(eventKey)
        print(eventKey)
        print(comments.count)
        messagesRef?.observe(.childAdded, with: { (snapshot) in
            print(snapshot)
            guard let commentDictionary = snapshot.value as? [String: Any] else{
                return
            }
            print(commentDictionary)
            guard let uid = commentDictionary["uid"] as? String else{
                return
            }
            UserService.show(forUID: uid, completion: { (user) in
                if let user = user {
                    var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                    commentFetched.commentID = snapshot.key
                    let filteredArr = self.comments.filter { (comment) -> Bool in
                        return comment.commentID == commentFetched.commentID
                    }
                    if filteredArr.count == 0 {
                        self.comments.append(commentFetched)
                    }
                    print(self.comments)
                    self.adapter.performUpdates(animated: true)
                }
                self.comments.sort(by: { (comment1, comment2) -> Bool in
                    return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
                })
                self.comments.forEach({ (comments) in
                })
            })
        }, withCancel: { (error) in
            print("Failed to observe comments")
        })

        //first lets fetch comments for current event
    }

This method handles fetching the comments and the following method is the datasource for the Controller

extension NewCommentsViewController: ListAdapterDataSource {
    // 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries.
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        var items:[ListDiffable] = comments
        print("comments = \(comments)")
        return [addHeader] + items
    }

    // 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller.
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
        if let object = object as? ListDiffable, object === addHeader {
            return CommentsHeaderSectionController()
        }
        return CommentsSectionController()
    }

    // 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature.
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.white
        return view
    }
}
weyert commented 7 years ago

Yes, I think via the collectionContext property of the section controller. If I were you would just go the easy way and go for the delegate pattern. Pass a delegate to your comments section controller and then when the Item needs to be deleted do it through this delegate and implement it in your VC so you can reuse your existing code.

If this doesn't make sense can you make a demo/example project so we can have a closer look what's happening?

Sent with GitHawk

Smiller193 commented 7 years ago

So, for example,If I am understanding correctly I would add a protocol/delegate to this function. Which is the comments section controller

import UIKit
import IGListKit
import Foundation

class CommentsSectionController: ListSectionController,CommentCellDelegate {
    var comment: CommentGrabbed?
    var eventKey: String?
    override init() {
        super.init()
        // supplementaryViewSource = self
        //sets the spacing between items in a specfic section controller
        inset = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0)
    }
    // MARK: IGListSectionController Overrides
    override func numberOfItems() -> Int {
        return 1
    }

    override func sizeForItem(at index: Int) -> CGSize {
        let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
        let dummyCell = CommentCell(frame: frame)
        dummyCell.comment = comment
        dummyCell.layoutIfNeeded()
        let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
        let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
        let height = max(40+8+8, estimatedSize.height)
        return  CGSize(width: collectionContext!.containerSize.width, height: height)

    }

    override var minimumLineSpacing: CGFloat {
        get {
            return 0.0
        }
        set {
            self.minimumLineSpacing = 0.0
        }
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else {
            fatalError()
        }
        //  print(comment)
        cell.comment = comment
        cell.delegate = self
        return cell
    }
    override func didUpdate(to object: Any) {
        comment = object as! CommentGrabbed
    }
    override func didSelectItem(at index: Int){
    }
    /*
     func supportedElementKinds() -> [String] {
     return [UICollectionElementKindSectionHeader]
     }
     func viewForSupplementaryElement(ofKind elementKind: String, at index: Int) -> UICollectionReusableView {
     guard let view = collectionContext?.dequeueReusableSupplementaryView(ofKind: elementKind, for: self, class: CommentHeader.self, at: index) as? CommentHeader else{
     fatalError()
     }
     view.handle = "Comments"
     return view
     }
     */
    //    func optionsButtonTapped(cell: CommentCell) {
    //        print("like")
    //
    //
    //    }

    func optionsButtonTapped(cell: CommentCell){
        print("like")
     //   guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self)(for: cell) else { return }
        guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self) else{
            return
        }
        // 2
        let comment = self.comment
        _ = comment?.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment?.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment!)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment!, (comment?.eventKey)!)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        self.viewController?.present(alertController, animated: true, completion: nil)

    }

    /*
     func sizeForSupplementaryView(ofKind elementKind: String, at index: Int) -> CGSize {
     return CGSize(width: collectionContext!.containerSize.width, height: 40)
     }
     */

}

And conform to that delegate in this controller

import UIKit
import IGListKit
import Firebase

class NewCommentsViewController: UIViewController, UITextFieldDelegate {
    //array of comments which will be loaded by a service function
    var comments = [CommentGrabbed]()
    var messagesRef: DatabaseReference?
    var bottomConstraint: NSLayoutConstraint?
    public let addHeader = "addHeader" as ListDiffable
    public var eventKey = ""
    //This creates a lazily-initialized variable for the IGListAdapter. The initializer requires three parameters:
    //1 updater is an object conforming to IGListUpdatingDelegate, which handles row and section updates. IGListAdapterUpdater is a default implementation that is suitable for your usage.
    //2 viewController is a UIViewController that houses the adapter. This view controller is later used for navigating to other view controllers.
    //3 workingRangeSize is the size of the working range, which allows you to prepare content for sections just outside of the visible frame.

    lazy var adapter: ListAdapter = {
        return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    }()

    // 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
    let collectionView: UICollectionView = {
        // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
        let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
        // 3 The background color is set to white
        view.backgroundColor = UIColor.white
        return view
    }()

    //will fetch the comments from the database and append them to an array
    fileprivate func fetchComments(){
        messagesRef = Database.database().reference().child("Comments").child(eventKey)
        print(eventKey)
        print(comments.count)
        messagesRef?.observe(.childAdded, with: { (snapshot) in
            print(snapshot)
            guard let commentDictionary = snapshot.value as? [String: Any] else{
                return
            }
            print(commentDictionary)
            guard let uid = commentDictionary["uid"] as? String else{
                return
            }
            UserService.show(forUID: uid, completion: { (user) in
                if let user = user {
                    var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                    commentFetched.commentID = snapshot.key
                    let filteredArr = self.comments.filter { (comment) -> Bool in
                        return comment.commentID == commentFetched.commentID
                    }
                    if filteredArr.count == 0 {
                        self.comments.append(commentFetched)
                    }
                    print(self.comments)
                    self.adapter.performUpdates(animated: true)
                }
                self.comments.sort(by: { (comment1, comment2) -> Bool in
                    return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
                })
                self.comments.forEach({ (comments) in
                })
            })
        }, withCancel: { (error) in
            print("Failed to observe comments")
        })

        //first lets fetch comments for current event
    }

    lazy var submitButton : UIButton = {
        let submitButton = UIButton(type: .system)
        submitButton.setTitle("Submit", for: .normal)
        submitButton.setTitleColor(.black, for: .normal)
        submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
        submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside)
        submitButton.isEnabled = false
        return submitButton
    }()

    //allows you to gain access to the input accessory view that each view controller has for inputting text
    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.addSubview(self.submitButton)
        self.submitButton.anchor(top: containerView.topAnchor, left: nil, bottom: containerView.bottomAnchor, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0)

        containerView.addSubview(self.commentTextField)
        self.commentTextField.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: containerView.bottomAnchor, right: self.submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
        self.commentTextField.delegate = self
        let lineSeparatorView = UIView()
        lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
        containerView.addSubview(lineSeparatorView)
        lineSeparatorView.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: nil, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)

        return containerView
    }()

    lazy var commentTextField: UITextField = {
        let textField = UITextField()
        textField.placeholder = "Add a comment"
        textField.delegate = self
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        return textField
    }()

    func textFieldDidChange(_ textField: UITextField) {
        let isCommentValid = commentTextField.text?.characters.count ?? 0 > 0
        if isCommentValid {
            submitButton.isEnabled = true
        }else{
            submitButton.isEnabled = false
        }
    }

    func handleSubmit(){
        guard let comment = commentTextField.text, comment.characters.count > 0 else{
            return
        }
        let userText = Comments(content: comment, uid: User.current.uid, profilePic: User.current.profilePic!,eventKey: eventKey)
        sendMessage(userText)
        // will remove text after entered
        self.commentTextField.text = nil
    }

    func flagButtonTapped (from cell: CommentCell){
        guard let indexPath = self.collectionView.indexPath(for: cell) else { return }

        // 2
        let comment = comments[indexPath.item]
        _ = comment.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment, self.eventKey)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
                self.adapter.performUpdates(animated: true)

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        present(alertController, animated: true, completion: nil)

    }

    func handleKeyboardNotification(notification: NSNotification){
        if let userinfo = notification.userInfo{

            let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            self.bottomConstraint?.constant = -(keyboardFrame.height)

            let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
            self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0

            UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
                self.view.layoutIfNeeded()
            }, completion: { (completion) in
                if self.comments.count > 0  && isKeyboardShowing {
                    let indexPath = IndexPath(item: self.comments.count-1, section: 0)
                    self.collectionView.scrollToItem(at: indexPath, at: .top, animated: true)
                }
            })
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        collectionView.addSubview(containerView)
        collectionView.alwaysBounceVertical = true
        containerView.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 40)
        //view.addConstraintsWithFormatt("H:|[v0]|", views: containerView)
        // view.addConstraintsWithFormatt("V:[v0(48)]", views: containerView)
        bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        view.addConstraint(bottomConstraint!)
        adapter.collectionView = collectionView
        adapter.dataSource = self
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "CommentCell")
        collectionView.register(CommentHeader.self, forCellWithReuseIdentifier: "HeaderCell")
        self.collectionView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)
        fetchComments()
        // Do any additional setup after loading the view.
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        tabBarController?.tabBar.isHidden = true
        submitButton.isUserInteractionEnabled = true

    }
    //viewDidLayoutSubviews() is overridden, setting the collectionView frame to match the view bounds.
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

extension NewCommentsViewController: ListAdapterDataSource {
    // 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries.
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        var items:[ListDiffable] = comments
        print("comments = \(comments)")
        return [addHeader] + items
    }

    // 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller.
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
        if let object = object as? ListDiffable, object === addHeader {
            return CommentsHeaderSectionController()
        }
        return CommentsSectionController()
    }

    // 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature.
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.white
        return view
    }
}

extension NewCommentsViewController {
    func sendMessage(_ message: Comments) {
        ChatService.sendMessage(message, eventKey: eventKey)

    }
}

Calling some function that I create as the delegate function that it conforms to and call the performUpdates function somewhere in this VC?

weyert commented 7 years ago

Yes, correct which then will trigger your code for removing the comment from your data source and then doing a refresh of the IGListKit by calling: self.adapter.performUpdates(animated: true) :)

Smiller193 commented 7 years ago

Okay cool thanks ill put that into practice and let you know how it goes

Smiller193 commented 7 years ago

This is my section controller

import UIKit
import IGListKit
import Foundation

protocol CommentsSectionDelegate: class {
    func CommentSectionUpdared(sectionController: CommentsSectionController)
}
class CommentsSectionController: ListSectionController,CommentCellDelegate {
    weak var delegate: CommentsSectionDelegate? = nil
    var comment: CommentGrabbed?
    var eventKey: String?
    override init() {
        super.init()
        // supplementaryViewSource = self
        //sets the spacing between items in a specfic section controller
        inset = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0)
    }
    // MARK: IGListSectionController Overrides
    override func numberOfItems() -> Int {
        return 1
    }

    override func sizeForItem(at index: Int) -> CGSize {
        let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
        let dummyCell = CommentCell(frame: frame)
        dummyCell.comment = comment
        dummyCell.layoutIfNeeded()
        let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
        let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
        let height = max(40+8+8, estimatedSize.height)
        return  CGSize(width: collectionContext!.containerSize.width, height: height)

    }

    override var minimumLineSpacing: CGFloat {
        get {
            return 0.0
        }
        set {
            self.minimumLineSpacing = 0.0
        }
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else {
            fatalError()
        }
        //  print(comment)
        cell.comment = comment
        cell.delegate = self
        return cell
    }
    override func didUpdate(to object: Any) {
        comment = object as! CommentGrabbed
    }
    override func didSelectItem(at index: Int){
    }
    /*
     func supportedElementKinds() -> [String] {
     return [UICollectionElementKindSectionHeader]
     }
     func viewForSupplementaryElement(ofKind elementKind: String, at index: Int) -> UICollectionReusableView {
     guard let view = collectionContext?.dequeueReusableSupplementaryView(ofKind: elementKind, for: self, class: CommentHeader.self, at: index) as? CommentHeader else{
     fatalError()
     }
     view.handle = "Comments"
     return view
     }
     */
    //    func optionsButtonTapped(cell: CommentCell) {
    //        print("like")
    //
    //
    //    }

    func optionsButtonTapped(cell: CommentCell){
        print("like")
     //   guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self)(for: cell) else { return }
        guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self) else{
            return
        }
        // 2
        let comment = self.comment
        _ = comment?.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment?.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment!)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment!, (comment?.eventKey)!)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
                self.onItemDeleted()

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        self.viewController?.present(alertController, animated: true, completion: nil)

    }
    func onItemDeleted() {
        delegate?.CommentSectionUpdared(sectionController: self)
    }

    /*
     func sizeForSupplementaryView(ofKind elementKind: String, at index: Int) -> CGSize {
     return CGSize(width: collectionContext!.containerSize.width, height: 40)
     }
     */

}

Umm the delgate function function seems to not be getting called this is my VC

import UIKit
import IGListKit
import Firebase

class NewCommentsViewController: UIViewController, UITextFieldDelegate,CommentsSectionDelegate {
    //array of comments which will be loaded by a service function
    var comments = [CommentGrabbed]()
    var messagesRef: DatabaseReference?
    var bottomConstraint: NSLayoutConstraint?
    public let addHeader = "addHeader" as ListDiffable
    public var eventKey = ""
    //This creates a lazily-initialized variable for the IGListAdapter. The initializer requires three parameters:
    //1 updater is an object conforming to IGListUpdatingDelegate, which handles row and section updates. IGListAdapterUpdater is a default implementation that is suitable for your usage.
    //2 viewController is a UIViewController that houses the adapter. This view controller is later used for navigating to other view controllers.
    //3 workingRangeSize is the size of the working range, which allows you to prepare content for sections just outside of the visible frame.

    lazy var adapter: ListAdapter = {
        return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    }()

    // 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
    let collectionView: UICollectionView = {
        // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
        let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
        // 3 The background color is set to white
        view.backgroundColor = UIColor.white
        return view
    }()

    //will fetch the comments from the database and append them to an array
    fileprivate func fetchComments(){
        messagesRef = Database.database().reference().child("Comments").child(eventKey)
        print(eventKey)
        print(comments.count)
        messagesRef?.observe(.childAdded, with: { (snapshot) in
            print(snapshot)
            guard let commentDictionary = snapshot.value as? [String: Any] else{
                return
            }
            print(commentDictionary)
            guard let uid = commentDictionary["uid"] as? String else{
                return
            }
            UserService.show(forUID: uid, completion: { (user) in
                if let user = user {
                    var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                    commentFetched.commentID = snapshot.key
                    let filteredArr = self.comments.filter { (comment) -> Bool in
                        return comment.commentID == commentFetched.commentID
                    }
                    if filteredArr.count == 0 {
                        self.comments.append(commentFetched)
                    }
                    print(self.comments)
                    self.adapter.performUpdates(animated: true)
                }
                self.comments.sort(by: { (comment1, comment2) -> Bool in
                    return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
                })
                self.comments.forEach({ (comments) in
                })
            })
        }, withCancel: { (error) in
            print("Failed to observe comments")
        })

        //first lets fetch comments for current event
    }

    lazy var submitButton : UIButton = {
        let submitButton = UIButton(type: .system)
        submitButton.setTitle("Submit", for: .normal)
        submitButton.setTitleColor(.black, for: .normal)
        submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
        submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside)
        submitButton.isEnabled = false
        return submitButton
    }()

    //allows you to gain access to the input accessory view that each view controller has for inputting text
    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.addSubview(self.submitButton)
        self.submitButton.anchor(top: containerView.topAnchor, left: nil, bottom: containerView.bottomAnchor, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0)

        containerView.addSubview(self.commentTextField)
        self.commentTextField.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: containerView.bottomAnchor, right: self.submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
        self.commentTextField.delegate = self
        let lineSeparatorView = UIView()
        lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
        containerView.addSubview(lineSeparatorView)
        lineSeparatorView.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: nil, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)

        return containerView
    }()

    lazy var commentTextField: UITextField = {
        let textField = UITextField()
        textField.placeholder = "Add a comment"
        textField.delegate = self
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        return textField
    }()

    func textFieldDidChange(_ textField: UITextField) {
        let isCommentValid = commentTextField.text?.characters.count ?? 0 > 0
        if isCommentValid {
            submitButton.isEnabled = true
        }else{
            submitButton.isEnabled = false
        }
    }

    func handleSubmit(){
        guard let comment = commentTextField.text, comment.characters.count > 0 else{
            return
        }
        let userText = Comments(content: comment, uid: User.current.uid, profilePic: User.current.profilePic!,eventKey: eventKey)
        sendMessage(userText)
        // will remove text after entered
        self.commentTextField.text = nil
    }

    func flagButtonTapped (from cell: CommentCell){
        guard let indexPath = self.collectionView.indexPath(for: cell) else { return }

        // 2
        let comment = comments[indexPath.item]
        _ = comment.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment, self.eventKey)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
                self.adapter.performUpdates(animated: true)

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        present(alertController, animated: true, completion: nil)

    }

    func handleKeyboardNotification(notification: NSNotification){
        if let userinfo = notification.userInfo{

            let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            self.bottomConstraint?.constant = -(keyboardFrame.height)

            let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
            self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0

            UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
                self.view.layoutIfNeeded()
            }, completion: { (completion) in
                if self.comments.count > 0  && isKeyboardShowing {
                    let indexPath = IndexPath(item: self.comments.count-1, section: 0)
                    self.collectionView.scrollToItem(at: indexPath, at: .top, animated: true)
                }
            })
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        collectionView.addSubview(containerView)
        collectionView.alwaysBounceVertical = true
        containerView.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 40)
        //view.addConstraintsWithFormatt("H:|[v0]|", views: containerView)
        // view.addConstraintsWithFormatt("V:[v0(48)]", views: containerView)
        bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        view.addConstraint(bottomConstraint!)
        adapter.collectionView = collectionView
        adapter.dataSource = self
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "CommentCell")
        collectionView.register(CommentHeader.self, forCellWithReuseIdentifier: "HeaderCell")
        self.collectionView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)
        fetchComments()
        // Do any additional setup after loading the view.
    }
    //look here
    func CommentSectionUpdared(sectionController: CommentsSectionController){
        print("like")
    self.adapter.performUpdates(animated: true)
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        tabBarController?.tabBar.isHidden = true
        submitButton.isUserInteractionEnabled = true

    }
    //viewDidLayoutSubviews() is overridden, setting the collectionView frame to match the view bounds.
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

extension NewCommentsViewController: ListAdapterDataSource {
    // 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries.
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        var items:[ListDiffable] = comments
        print("comments = \(comments)")
        return [addHeader] + items
    }

    // 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller.
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
        if let object = object as? ListDiffable, object === addHeader {
            return CommentsHeaderSectionController()
        }
        return CommentsSectionController()
    }

    // 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature.
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.white
        return view
    }
}

extension NewCommentsViewController {
    func sendMessage(_ message: Comments) {
        ChatService.sendMessage(message, eventKey: eventKey)

    }
}

Notice any problems

weyert commented 7 years ago

Yes, you aren't setting the delegate-property when creating the instance of CommentsSectionController try:

let sectionController = CommentsSectionController()
sectionController.delegate = self
return sectionController

You could also change the code a bit so that this section controller accepts a delegate-parameter in it's initialiser so the code would become: return CommentsSectionController(delegate: self).

Smiller193 commented 7 years ago

Where would this code be inside of? would creating a new instance everytime not create a memory leak?

Smiller193 commented 7 years ago

oh inside the listadapter function

Smiller193 commented 7 years ago

So the updated version should look like this

import UIKit
import IGListKit
import Foundation

protocol CommentsSectionDelegate: class {
    func CommentSectionUpdared(sectionController: CommentsSectionController)
}
class CommentsSectionController: ListSectionController,CommentCellDelegate {
    weak var delegate: CommentsSectionDelegate? = nil
    var comment: CommentGrabbed?
    var eventKey: String?
    override init() {
        super.init()
        // supplementaryViewSource = self
        //sets the spacing between items in a specfic section controller
        inset = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0)
    }
    // MARK: IGListSectionController Overrides
    override func numberOfItems() -> Int {
        return 1
    }

    override func sizeForItem(at index: Int) -> CGSize {
        let frame = CGRect(x: 0, y: 0, width: collectionContext!.containerSize.width, height: 50)
        let dummyCell = CommentCell(frame: frame)
        dummyCell.comment = comment
        dummyCell.layoutIfNeeded()
        let targetSize =  CGSize(width: collectionContext!.containerSize.width, height: 55)
        let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
        let height = max(40+8+8, estimatedSize.height)
        return  CGSize(width: collectionContext!.containerSize.width, height: height)

    }

    override var minimumLineSpacing: CGFloat {
        get {
            return 0.0
        }
        set {
            self.minimumLineSpacing = 0.0
        }
    }

    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let cell = collectionContext?.dequeueReusableCell(of: CommentCell.self, for: self, at: index) as? CommentCell else {
            fatalError()
        }
        //  print(comment)
        cell.comment = comment
        cell.delegate = self
        return cell
    }
    override func didUpdate(to object: Any) {
        comment = object as! CommentGrabbed
    }
    override func didSelectItem(at index: Int){
    }
    /*
     func supportedElementKinds() -> [String] {
     return [UICollectionElementKindSectionHeader]
     }
     func viewForSupplementaryElement(ofKind elementKind: String, at index: Int) -> UICollectionReusableView {
     guard let view = collectionContext?.dequeueReusableSupplementaryView(ofKind: elementKind, for: self, class: CommentHeader.self, at: index) as? CommentHeader else{
     fatalError()
     }
     view.handle = "Comments"
     return view
     }
     */
    //    func optionsButtonTapped(cell: CommentCell) {
    //        print("like")
    //
    //
    //    }

    func optionsButtonTapped(cell: CommentCell){
        print("like")
     //   guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self)(for: cell) else { return }
        guard let indexPath = self.collectionContext?.index(for: cell, sectionController: self) else{
            return
        }
        // 2
        let comment = self.comment
        _ = comment?.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment?.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment!)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment!, (comment?.eventKey)!)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.viewController?.present(okAlert, animated: true, completion: nil)
                self.onItemDeleted()

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        self.viewController?.present(alertController, animated: true, completion: nil)

    }
    func onItemDeleted() {
        delegate?.CommentSectionUpdared(sectionController: self)
    }

    /*
     func sizeForSupplementaryView(ofKind elementKind: String, at index: Int) -> CGSize {
     return CGSize(width: collectionContext!.containerSize.width, height: 40)
     }
     */

}

And then the VC

import UIKit
import IGListKit
import Firebase

class NewCommentsViewController: UIViewController, UITextFieldDelegate,CommentsSectionDelegate {
    //array of comments which will be loaded by a service function
    var comments = [CommentGrabbed]()
    var messagesRef: DatabaseReference?
    var bottomConstraint: NSLayoutConstraint?
    public let addHeader = "addHeader" as ListDiffable
    public var eventKey = ""
    //This creates a lazily-initialized variable for the IGListAdapter. The initializer requires three parameters:
    //1 updater is an object conforming to IGListUpdatingDelegate, which handles row and section updates. IGListAdapterUpdater is a default implementation that is suitable for your usage.
    //2 viewController is a UIViewController that houses the adapter. This view controller is later used for navigating to other view controllers.
    //3 workingRangeSize is the size of the working range, which allows you to prepare content for sections just outside of the visible frame.

    lazy var adapter: ListAdapter = {
        return ListAdapter(updater: ListAdapterUpdater(), viewController: self)
    }()

    // 1 IGListKit uses IGListCollectionView, which is a subclass of UICollectionView, which patches some functionality and prevents others.
    let collectionView: UICollectionView = {
        // 2 This starts with a zero-sized rect since the view isn’t created yet. It uses the UICollectionViewFlowLayout just as the ClassicFeedViewController did.
        let view = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
        // 3 The background color is set to white
        view.backgroundColor = UIColor.white
        return view
    }()

    //will fetch the comments from the database and append them to an array
    fileprivate func fetchComments(){
        messagesRef = Database.database().reference().child("Comments").child(eventKey)
        print(eventKey)
        print(comments.count)
        messagesRef?.observe(.childAdded, with: { (snapshot) in
            print(snapshot)
            guard let commentDictionary = snapshot.value as? [String: Any] else{
                return
            }
            print(commentDictionary)
            guard let uid = commentDictionary["uid"] as? String else{
                return
            }
            UserService.show(forUID: uid, completion: { (user) in
                if let user = user {
                    var commentFetched = CommentGrabbed(user: user, dictionary: commentDictionary)
                    commentFetched.commentID = snapshot.key
                    let filteredArr = self.comments.filter { (comment) -> Bool in
                        return comment.commentID == commentFetched.commentID
                    }
                    if filteredArr.count == 0 {
                        self.comments.append(commentFetched)
                    }
                    print(self.comments)
                    self.adapter.performUpdates(animated: true)
                }
                self.comments.sort(by: { (comment1, comment2) -> Bool in
                    return comment1.creationDate.compare(comment2.creationDate) == .orderedAscending
                })
                self.comments.forEach({ (comments) in
                })
            })
        }, withCancel: { (error) in
            print("Failed to observe comments")
        })

        //first lets fetch comments for current event
    }

    lazy var submitButton : UIButton = {
        let submitButton = UIButton(type: .system)
        submitButton.setTitle("Submit", for: .normal)
        submitButton.setTitleColor(.black, for: .normal)
        submitButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
        submitButton.addTarget(self, action: #selector(handleSubmit), for: .touchUpInside)
        submitButton.isEnabled = false
        return submitButton
    }()

    //allows you to gain access to the input accessory view that each view controller has for inputting text
    lazy var containerView: UIView = {
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.addSubview(self.submitButton)
        self.submitButton.anchor(top: containerView.topAnchor, left: nil, bottom: containerView.bottomAnchor, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 12, width: 50, height: 0)

        containerView.addSubview(self.commentTextField)
        self.commentTextField.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: containerView.bottomAnchor, right: self.submitButton.leftAnchor, paddingTop: 0, paddingLeft: 12, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
        self.commentTextField.delegate = self
        let lineSeparatorView = UIView()
        lineSeparatorView.backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
        containerView.addSubview(lineSeparatorView)
        lineSeparatorView.anchor(top: containerView.topAnchor, left: containerView.leftAnchor, bottom: nil, right: containerView.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0.5)

        return containerView
    }()

    lazy var commentTextField: UITextField = {
        let textField = UITextField()
        textField.placeholder = "Add a comment"
        textField.delegate = self
        textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
        return textField
    }()

    func textFieldDidChange(_ textField: UITextField) {
        let isCommentValid = commentTextField.text?.characters.count ?? 0 > 0
        if isCommentValid {
            submitButton.isEnabled = true
        }else{
            submitButton.isEnabled = false
        }
    }

    func handleSubmit(){
        guard let comment = commentTextField.text, comment.characters.count > 0 else{
            return
        }
        let userText = Comments(content: comment, uid: User.current.uid, profilePic: User.current.profilePic!,eventKey: eventKey)
        sendMessage(userText)
        // will remove text after entered
        self.commentTextField.text = nil
    }

    func flagButtonTapped (from cell: CommentCell){
        guard let indexPath = self.collectionView.indexPath(for: cell) else { return }

        // 2
        let comment = comments[indexPath.item]
        _ = comment.uid

        // 3
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        // 4
        if comment.uid != User.current.uid {
            let flagAction = UIAlertAction(title: "Report as Inappropriate", style: .default) { _ in
                ChatService.flag(comment)

                let okAlert = UIAlertController(title: nil, message: "The post has been flagged.", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
            }
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            alertController.addAction(flagAction)
        }else{
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
            let deleteAction = UIAlertAction(title: "Delete Comment", style: .default, handler: { _ in
                ChatService.deleteComment(comment, self.eventKey)
                let okAlert = UIAlertController(title: nil, message: "Comment Has Been Deleted", preferredStyle: .alert)
                okAlert.addAction(UIAlertAction(title: "Ok", style: .default))
                self.present(okAlert, animated: true)
                self.adapter.performUpdates(animated: true)

            })
            alertController.addAction(cancelAction)
            alertController.addAction(deleteAction)

        }
        present(alertController, animated: true, completion: nil)

    }

    func handleKeyboardNotification(notification: NSNotification){
        if let userinfo = notification.userInfo{

            let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            self.bottomConstraint?.constant = -(keyboardFrame.height)

            let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
            self.bottomConstraint?.constant = isKeyboardShowing ? -(keyboardFrame.height) : 0

            UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
                self.view.layoutIfNeeded()
            }, completion: { (completion) in
                if self.comments.count > 0  && isKeyboardShowing {
                    let indexPath = IndexPath(item: self.comments.count-1, section: 0)
                    self.collectionView.scrollToItem(at: indexPath, at: .top, animated: true)
                }
            })
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        collectionView.addSubview(containerView)
        collectionView.alwaysBounceVertical = true
        containerView.anchor(top: nil, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 40)
        //view.addConstraintsWithFormatt("H:|[v0]|", views: containerView)
        // view.addConstraintsWithFormatt("V:[v0(48)]", views: containerView)
        bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        view.addConstraint(bottomConstraint!)
        adapter.collectionView = collectionView
        adapter.dataSource = self
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        collectionView.register(CommentCell.self, forCellWithReuseIdentifier: "CommentCell")
        collectionView.register(CommentHeader.self, forCellWithReuseIdentifier: "HeaderCell")
        self.collectionView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)
        fetchComments()
        // Do any additional setup after loading the view.
    }
    //look here
    func CommentSectionUpdared(sectionController: CommentsSectionController){
        print("like")
    self.adapter.performUpdates(animated: true)
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        tabBarController?.tabBar.isHidden = true
        submitButton.isUserInteractionEnabled = true

    }
    //viewDidLayoutSubviews() is overridden, setting the collectionView frame to match the view bounds.
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

extension NewCommentsViewController: ListAdapterDataSource {
    // 1 objects(for:) returns an array of data objects that should show up in the collection view. loader.entries is provided here as it contains the journal entries.
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        var items:[ListDiffable] = comments
        print("comments = \(comments)")
        return [addHeader] + items
    }

    // 2 For each data object, listAdapter(_:sectionControllerFor:) must return a new instance of a section controller. For now you’re returning a plain IGListSectionController to appease the compiler — in a moment, you’ll modify this to return a custom journal section controller.
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        //the comment section controller will be placed here but we don't have it yet so this will be a placeholder
        if let object = object as? ListDiffable, object === addHeader {
            return CommentsHeaderSectionController()
        }
        let sectionController = CommentsSectionController()
        sectionController.delegate = self
        return sectionController
    }

    // 3 emptyView(for:) returns a view that should be displayed when the list is empty. NASA is in a bit of a time crunch, so they didn’t budget for this feature.
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        let view = UIView()
        view.backgroundColor = UIColor.white
        return view
    }
}

extension NewCommentsViewController {
    func sendMessage(_ message: Comments) {
        ChatService.sendMessage(message, eventKey: eventKey)

    }
}
Smiller193 commented 7 years ago

The comment still seems to be there how would I update screen and datasource

weyert commented 7 years ago

Can you please make a reproducible version of your problem in a git repo so we can have a look at it?

Smiller193 commented 7 years ago

https://github.com/Smiller193/EventfulApp1

Smiller193 commented 7 years ago

Those two files are in there

weyert commented 7 years ago

@Smiller193 Looks like you are only calling CommentSectionUpdared in the section controller but not actually deleting the comment anywhere? You probably need to call fetchComments again or just filter out the deleted comment from the comments-variable.

If this array doesn't change it won't disappear from the list when you call self.adapter.performUpdates(animated: true). As IGListKit is smart enough to avoid refreshing the collection view when no data has changed. I haven't used Firebase personally yet but I assume you somehow can get notified when the comments have changed and ensure to fetch the new comments or all comments. You could go for the optimistic route and just filter out the comment from the existing comment.

The following might solve your issue quickly, I haven't tested it as I don't have login credentials for your project:

    func CommentSectionUpdared(sectionController: CommentsSectionController){
        print("like")
        self.fetchComments()
    }
Smiller193 commented 7 years ago

well I know what the problem may be it seems to not even be deleting it from firebase

Smiller193 commented 7 years ago

Got it to work