iOS Swift Tutorial iOS8 Xcode App Design YikYak

Using UITableView and FooterView to implement commenting system: Building Yik Yak Clone Part 3

This is the part 3 of Building the Yik Yak Clone tutorial series. In this part of the tutorial we will be implementing commenting system on post/yak messages using UITableview and Footerview. If you haven’t gone through the Building Yik Yak Clone Part 1 and Building Yik Yak Clone Part 2 I will suggest you to go through them first before continuing.

Xcode 6 and Interface builder setup

  • Drag a UIViewController on to the main storyboard
  • Embed the UIViewController in Navigation Controller.
  • Drag a two UILabel on the UIViewController.
  • Drag a mapview on the UIViewController.
  • Drag a table view below the map and a UITableViewCell in the table view.
  • Drag from UITableViewCell of the previous yak listing controller to this
    images

Create custom DetailViewController and CommentTableViewCell

Create new view controller DetailViewController of type UIViewController and a custom tableview cell CommentTableView of type UITableViewCell.

Setup the IBoutlet for yakText, timeLabel , mapView and tableView in DetailViewController. And IBOutlet commentText in CommentTableViewCell.

Setup the constraints for all the elements as below.

images

images

images

images

images

In the detail view controller there are a static elements for displaying for the yak message , date and a map view. We will be using the table view for displaying the comments for the post.

UITableView provide mechanism to add custom footer and header view. In our case we will be using the footer view to provide text view for commenting. We need to take care of an issue where the keyboard could hide the text view and prevent us from commenting. To get around this problem we will be using the Keyboard notifications to prevent keyboard from covering the text view Receiving Keyboard Notifications.

NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

We will be using NSNotificationCenter which broadcasts observers about any event that happen and the app is interested in. The events acts as a customization hook to modify the behaviour of how the app should work on these events. In our case we will tell the NotificationCenter to call KeyBoardWillShow and keyBoardWillHide methods on the UIKeyboardWillShowNotification and UIKeyboardWillHideNotification events accordingly.

In KeyBoardWillShow method we will find the keyboard height and UIEdgeInsets accordingly to scroll the content above the keyboard.

In KeyBoardWillHide method we will reset the contentInsets undoing what we did in the keyBoardWillShow method.

I am also substracting the extra 40px without which the content wouldn’t be offset correctly. I dont need to substract the extra 40px when I drag in the UITableViewController directly. This seems to be a issue when you implement the UITableViewDataSource and UITableViewDelegate yourself. I found that we people were offsetting it by 40 to 44px on stackoverflow. This might not be the correct way to implement but if you have a better way to fix please let me know.

More on UIScrollView

//
//  DetailViewController.swift
//  YikYak
//
//  Created by Shrikar Archak on 1/6/15.
//  Copyright (c) 2015 Shrikar Archak. All rights reserved.
//

import UIKit
import MapKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextViewDelegate {

    var yak: PFObject?
    var commentView: UITextView?
    var footerView: UIView?
    var contentHeight: CGFloat = 0

    var comments: [String]?
    let FOOTERHEIGHT : CGFloat = 50;

    @IBOutlet weak var mapView: MKMapView!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var timeLabel: UILabel!
    @IBOutlet weak var yakLabel: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()

        /* Setup the datasource delegate */
        tableView.delegate = self
        tableView.dataSource = self

        /* Setup the keyboard notifications */
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardWillHide:", name: UIKeyboardWillHideNotification, object: nil)


        /* Setup the contentInsets */
        self.tableView.contentInset = UIEdgeInsetsZero
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero


        self.edgesForExtendedLayout = UIRectEdge.None
        /* Make sure the content doesn't go below tabbar/navbar */
        self.extendedLayoutIncludesOpaqueBars = true

        self.automaticallyAdjustsScrollViewInsets = false


        /* Setup Map */
        let geo = yak?.objectForKey("location") as PFGeoPoint
        let coordinate = CLLocationCoordinate2D(latitude: geo.latitude, longitude: geo.longitude)
        let reg = MKCoordinateRegionMakeWithDistance(coordinate, 1500, 1500)
        self.mapView.setRegion(reg, animated: true)
        self.mapView.showsUserLocation = true


        if(yak?.objectForKey("comments") != nil) {
            comments = yak?.objectForKey("comments") as [String]
        }
        println(yak)
        println(yak?.objectForKey("text"))
        self.yakLabel.text = yak?.objectForKey("text") as String
    }

    func keyBoardWillShow(notification: NSNotification) {
        var info:NSDictionary = notification.userInfo!
        var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as NSValue).CGRectValue()

        var keyboardHeight:CGFloat =  keyboardSize.height - 40

        var animationDuration:CGFloat = info[UIKeyboardAnimationDurationUserInfoKey] as CGFloat

        var contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardHeight, 0.0);
        self.tableView.contentInset = contentInsets
        self.tableView.scrollIndicatorInsets = contentInsets

    }

    func keyBoardWillHide(notification: NSNotification) {

        self.tableView.contentInset = UIEdgeInsetsZero
        self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }


    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let count = comments?.count {
            return count
        }
        return 0
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }


    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("commentCell", forIndexPath: indexPath) as CommentTableViewCell
        cell.commentText?.text = comments![indexPath.row]
        return cell
    }

    func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        if self.footerView != nil {
            return self.footerView!.bounds.height
        }
        return FOOTERHEIGHT
    }


    func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: FOOTERHEIGHT))
        footerView?.backgroundColor = UIColor(red: 243.0/255, green: 243.0/255, blue: 243.0/255, alpha: 1)
        commentView = UITextView(frame: CGRect(x: 10, y: 5, width: tableView.bounds.width - 80 , height: 40))
        commentView?.backgroundColor = UIColor.whiteColor()
        commentView?.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)
        commentView?.layer.cornerRadius = 2
        commentView?.scrollsToTop = true

        footerView?.addSubview(commentView!)
        let button = UIButton(frame: CGRect(x: tableView.bounds.width - 65, y: 10, width: 60 , height: 30))
        button.setTitle("Reply", forState: UIControlState.Normal)
        button.backgroundColor = UIColor(red: 155.0/255, green: 189.0/255, blue: 113.0/255, alpha: 1)
        button.layer.cornerRadius = 5
        button.addTarget(self, action: "reply", forControlEvents: UIControlEvents.TouchUpInside)
        footerView?.addSubview(button)
        commentView?.delegate = self
        return footerView
    }

    func textViewDidChange(textView: UITextView) {


        if (contentHeight == 0) {
            contentHeight = commentView!.contentSize.height
        }

        if(commentView!.contentSize.height != contentHeight && commentView!.contentSize.height > footerView!.bounds.height) {
            UIView.animateWithDuration(0.2, animations: { () -> Void in
                let myview = self.footerView
                println(self.commentView!.contentSize.height)
                println(self.commentView?.font.lineHeight)
                let newHeight : CGFloat = self.commentView!.font.lineHeight
                let myFrame = CGRect(x: myview!.frame.minX, y: myview!.frame.minY - newHeight , width: myview!.bounds.width, height: newHeight + myview!.bounds.height)
                myview?.frame = myFrame

                let mycommview = self.commentView
                let newCommHeight : CGFloat = self.commentView!.contentSize.height
                let myCommFrame = CGRect(x: mycommview!.frame.minX, y: mycommview!.frame.minY, width: mycommview!.bounds.width, height: newCommHeight)
                mycommview?.frame = myCommFrame

                self.commentView = mycommview
                self.footerView  = myview

                for item in self.footerView!.subviews {
                    if(item.isKindOfClass(UIButton.self)){
                        let button = item as UIButton
                        let newY = self.footerView!.bounds.height / 2 - button.bounds.height / 2
                        let buttonFrame = CGRect(x: button.frame.minX, y: newY , width: button.bounds.width, height : button.bounds.height)
                        button.frame = buttonFrame

                    }
                }
            })

            println(self.footerView?.frame)
            println(self.commentView?.frame)
            contentHeight = commentView!.contentSize.height
        }


    }

    func reply() {
        yak?.addObject(commentView?.text, forKey: "comments")
        yak?.saveInBackground()
        if let tmpText = commentView?.text {
            comments?.append(tmpText)
        }
        commentView?.text = ""
        println(comments?.count)
        self.commentView?.resignFirstResponder()
        self.tableView.reloadData()
    }
}

Final App

So we have build the Listing of yaks, Posting a Yak, Implementing commenting. There is one more feature which is creating the setting tab which you can find out how to implement here Xcode 6 Grouped TableViews

images

images
Please let me know if you have any question / feedback. If you have followed along the tutorial series let me know how it went in the comments sections.

About the author

Shrikar

Backend/Infrastructure Engineer by Day. iOS Developer for the rest of the time.

  • Ace Corpuz

    can you put on the download file for the finished project?

  • Riya Berry

    What does &amp mean? Is that outdated language?

  • Riya Berry

    I mean &amp

  • Riya Berry

    sorry it wont let me type the full thing but it is “&” “a” “m” “p” all together

  • http://shrikar.com shrikar

    I am sorry Riya the code is not formatted correctly. Replace -&gt with ->gt

  • http://shrikar.com shrikar

    I am sorry the code is not formatted correctly. “-&gt” should be replace by -&gt

  • Riya Berry

    Is there any chance you could post a separate page on parse and the classes you used for this yik yak clone?

  • Greg

    Hi, can I use this code in my own app?

    • http://shrikar.com shrikar

      Sure!

  • Dane Jordan

    Hey Shrikar,

    Thanks for the great tutorial! Couple questions, could I use this in my app and how would you suggest limiting upvoting and downvoting to one per yak like in the actual app?

  • Anonymous

    I cannot get the comments to work. It doesn’t even display the post on the new ViewController

  • Akshat Jagga

    The github YikYak project that you have uploaded does not work with Xcode 7 and shows that libpods.a error.

/* ]]> */