TrashMapper - iOS Project - Development - Part7
Table of Contents
Updates
Made some additional updates to the CreatePostViewController.
- Keyboard now dismisses when the user taps away from the UITextFieldView
- A character counter is visible on the UITextField view and limits the character count to 100
- May reduce to keep descriptions short
- Date formatter no longer adds a time in, only the month, day, year.
- Fill in UITextField with placeholder text. When tapped, change the font color to black clear text view.
- If no text entered, and user tapped away, refill with placeholder text
- Added a constants file to prevent file depending naming/values
Code for CreatePostViewController
// CreatePostViewController.swift
// TrashMapper
//
// Created by Jacob Case on 5/28/22.
//
import UIKit
import CoreLocation
import MapKit
//Create outside viewController class to only instantiate a single formatter to be used everytime
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
class CreatePostViewController: UITableViewController {
//outlets for main screen
@IBOutlet weak var addPhotoButton: UIButton!
@IBOutlet weak var descriptionTextView: UITextView!
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var numCharsLeft: UILabel!
//grab junk photo for demonstration
let bundleImage: UIImage? = UIImage(named: "trash_in_park.jpg")
//image variable for picker and delegates
var image: UIImage?
//variable for segue data transfer, test post
var coordinate = CLLocationCoordinate2D (latitude: 0, longitude: 0)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//create gesture recognizer for tap outside of UITextView
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
gestureRecognizer.cancelsTouchesInView = false
tableView.addGestureRecognizer(gestureRecognizer)
//Load information for a bogus/test location
dateLabel.text = format(date: Date())
//UITextView Setup
descriptionTextView.text = K.descriptionTextFieldText
descriptionTextView.textColor = UIColor.lightGray
//Delegate assignments
descriptionTextView.delegate = self
//max char length allowed for description text
numCharsLeft.text = K.numCharsLeft
//ISSUE - addPhotoButton.addDashedBorder() does not work, doesn't cover entire frame.
//addPhotoButton.addDashedBorder()
//ToDo
//App Looks better in white for demonstration
//Update later with custom Xibs, colors, etc
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.isNavigationBarHidden = false
navigationItem.title = K.createPostViewTitle
}
Helper methods to setup the date formatter and dismissal of keyboard.
//MARK: - Helper Methods
func format(date: Date) -> String {
return dateFormatter.string(from: date)
}
@objc func hideKeyboard(_ gestureRecognizer: UIGestureRecognizer) {
//refernce point from user tap
let point = gestureRecognizer.location(in: tableView)
//match point from tap to location on tableView
let indexPath = tableView.indexPathForRow(at: point)
//if user tap anywhere outide of UITextView, dismiss keybaord
if let indexPath = indexPath {
if indexPath.row != 3 {
descriptionTextView.resignFirstResponder()
}
}
}
}
//MARK: - Data Manager Tasks (Firebase)
/*
*/
Created an extension to track button functionality. I don’t believe this is typical architecture but I did this just to organize things for now.
//MARK: - Button functions
extension CreatePostViewController {
@IBAction func addPhotoButton(_ sender: Any) {
print("add photo tapped")
pickPhoto()
}
@IBAction func cancel(_ sender: Any) {
print("cancel tapped")
navigationController?.popViewController(animated: true)
//move back to previous view controller
//ping location?
//remove picture, and other potential post information
}
@IBAction func submit(_ sender: Any) {
print("submit tapped")
//create a userPost (date, image, description, user info, postID, etc)
//push to firebase
//HUD view with "Location Added"
}
}
Below method activates the keyboard when the UITextView is tapped by user.
//MARK: - Table View Methods
extension CreatePostViewController {
//Keyboard activation for description area
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 && indexPath.row == 3 {
descriptionTextView.becomeFirstResponder()
}
}
}
Below are methods for both delegate, alertController, and image selection.
//MARK: - Image Picker and Selection
extension CreatePostViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//After photo picked, grap photo from infokeys
image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
if let theImage = image {
//display photo within button "addPhoto"
displayImageOnButton(theImage)
/*
To Dos:
function to grab directory of app
assign unique UUID to photo (better strategy?)
store a compressed image within file via URL (as big as AddPhoto button for consistancy)
store location of that image, wiping if we cancel out or select different image
*/
}
dismiss(animated: true, completion: nil)
}
func takePhotoWithCamera() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func choosePhotoFromLibrary() {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func pickPhoto() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
showPhotoSelectionMenu()
} else {
choosePhotoFromLibrary()
}
}
func showPhotoSelectionMenu() {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let actCancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(actCancel)
let actPhoto = UIAlertAction(title: "Take Photo", style: .default, handler: { _ in self.takePhotoWithCamera()})
alert.addAction(actPhoto)
let actLibrary = UIAlertAction(title: "Choose From Library", style: .default, handler: { _ in self.choosePhotoFromLibrary()})
alert.addAction(actLibrary)
present(alert, animated: true)
}
func displayImageOnButton(_ image: UIImage) {
addPhotoButton.setImage(image, for: .normal)
}
}
Below are customized delegate methods that are used:
- Add placeholder text to UITextView if nothing present
- Auto empty placeholder text when UITextView selected
- Keep track of characters entered in the UITextView
//MARK: - Text View Delegate
extension CreatePostViewController : UITextViewDelegate {
//if textview empty, display a placeholder text
func textViewDidEndEditing(_ textView: UITextView) {
if textView.text.isEmpty {
textView.text = "Enter Description Here"
textView.textColor = UIColor.lightGray
}
}
//once user starts typing, change text to black
func textViewDidBeginEditing(_ textView: UITextView) {
if textView.textColor == .lightGray {
textView.text = nil
textView.textColor = UIColor.black
}
}
//show number of characters left for description field
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
//if no text in the view, return
guard let textField = textView.text else {return true}
//grab the current text count, replacement count, and desired range as a length
let charLength = K.charLength - (textField.count + text.count - range.length)
//update the cell detail label
numCharsLeft.text = "Characters left: \(charLength)"
//only allow text update if under 100 chars
return charLength <= 99
}
}
Simulation on Phone
The simulation below shows an inital loading of the app with user authorization already selected.
In the latter half of the gif, you can see the CreatePostViewController coming along.
