Creating a post

We are going to use an external library to handle the complex process of rendering all the user's media, picking an image, and applying a filter for us. The name of the library is YPImagePicker. This is an open source library that implements an Instagram-like photo and video picker in Swift. Instead of spending too much time in this book developing such a component, we are going to use one off the shelf and focus on implementing the rest of the application. Add the library to the Podfile, by inserting the pod 'YPImagePicker' line to the file. Then install the new pod like we have done before.

If you are interested in how to implement such a picker, you can take a look at the YPImagePicker source code here: 
https://github.com/Yummypets/YPImagePicker/tree/master/Source

We are hijacking the middle tabbar button action.  The pesentPicker function is called once we detect that the button is clicked. The following code shows the custom image picker view controller:

func pesentPicker() {
var config = YPImagePickerConfiguration()
config.onlySquareImagesFromLibrary = false
config.onlySquareImagesFromCamera = true
config.libraryTargetImageSize = .original
config.usesFrontCamera = true
config.showsFilters = true
config.shouldSaveNewPicturesToAlbum = !true
config.albumName = "IstagramLikeApp"
config.startOnScreen = .library

let picker = YPImagePicker(configuration: config)
picker.didSelectImage = {
img in
//TODO: ...
}

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

If we try to run the app, it will crash. iOS has to ask the user for appropriate permissions to access the user's photos. To do so, it needs special keys to be defined in the  Info.plist  file and associated with short description texts. The problem is that we should let the user know why the app needs access to the Photos library, device camera, and microphone. Thus, we have to add a description of why the app needs those. A system alert will be displayed and the user should agree before moving forward. If the user declines, then we should handle this case and let them know that this is a critical part of our app, without which it can't function properly.
To add these descriptions, you have to open the Info.plist file. Then, start adding the following keys and values:

<key>NSCameraUsageDescription</key>
<string>InstagramLike app needs access to your camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>InstagramLike app needs access to your photos.</string>
<key>NSMicrophoneUsageDescription</key>
<string>InstagramLike app needs access to your microphone.</string>

You can start typing the key and Xcode will help you. Another option is to enter the keys in the .plist file if you have opened it like a regular text file. Here is the final result:

The new permissions are added at the top of the main .plist file.
When the descriptions are added, the application should function, but once we pick a nice image and apply a filter, we can't leave the picker. The problem lies in the picker.didSelectImage closure. We have to dismiss the screen manually when an image is picked. If you start the app and press the camera button, you will see screens that show your photo gallery images in a grid layout. Here is what it looks like on a simulator of iPhone 6s:

The powerful picker component doesn't implement the last step, which is adding a caption and the actual post sharing when you press Done. We have to create a new screen to do that. Let's start with the UI first. Open the storyboard and create a new view controller:

Then, add a UIImageView with a width and height equal to 110 points. Next to it, place a TextView, which will allow the user to add a short description to the images.

Points are mapped to different numbers of pixels on each device, based on the device screen density  (pixels per inch). On some old devices, 1 point was 1 pixel, but on the retina ones, 1 point could be 4  pixels or more.

Here are the constraints that should be added so that the UIImageView has a fixed size and the TextView is filling the rest of the screen. You can use 15 points or more.
You need a new Swift file, CreatePostViewController.swift, and a new class, CreatePostViewController, which should be associated with the new view controller. Don't forget to set the storyboard ID of that controller, similar to what we did at the beginning of this chapter for a couple of other controllers:

class CreatePostViewController: UIViewController {    
override var prefersStatusBarHidden: Bool { return true }
private let placeholderText = "Write a caption..."
public var image:UIImage?
@IBOutlet weak var photo: UIImageView!
@IBOutlet weak var textView: UITextView! {
didSet {
textView.textColor = .gray
textView.text = placeholderText
textView.selectedRange = NSRange()
}
}

override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
photo.image = image
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "Share", style: .done, target: self,
action: #selector(createPost))
}
@objc func createPost() {
self.dismiss(animated: true, completion: nil)
}
}

 The view controller adds a Share button navigation item at the top right. The button should create the new post and close the picker, bringing the user back to their previous activity.

The actual code that creates the post and saves it on the Firebase server will be added later.

To add a placeholder text to the TextView competent, we have to add the following extension to our view controller. It's a pretty simple and short workaround that makes the TextView behave more like TextInput.

This is an extension to the CreatePostViewController and we can add it to the bottom of CreatePostViewController.swift. The placeholder property is part of the main class, because it can't be part of the extension. Only read-only properties can be part of extensions. We define the functions that will present the placeholder on the screen in the following code snippet:

extension CreatePostViewController: UITextViewDelegate {
func textViewDidChangeSelection(_ textView: UITextView) {
// Move cursor to beginning on first tap
if textView.text == placeholderText {
textView.selectedRange = NSRange()
}
}

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if textView.text == placeholderText && !text.isEmpty {
textView.text = nil
textView.textColor = .black
textView.selectedRange = NSRange()
}
return true
}

func textViewDidChange(_ textView: UITextView) {
if textView.text.isEmpty {
textView.textColor = .gray
textView.text = placeholderText
}
}
}

TextView decides what text to render based on what has been entered by the user. If the text is empty, the placeholder text is rendered. If the user enters any text, then that text is displayed. The selection is managed accordingly.
Now, let's try to hook our new CreatePostViewController to the picker. This will be done in InstagramTabbarController. We have to handle that in the didSelectImage closure:

picker.didSelectImage = {
[unowned picker, weak self] img in

if let viewController = self?.storyboard?
.instantiateViewController(withIdentifier: "CreatePost")
as? CreatePostViewController {

viewController.image = img

// Use Fade transition instead of default push animation
let transition = CATransition()
transition.duration = 0.3
transition.timingFunction = CAMediaTimingFunction(
name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
picker.view.layer.add(transition, forKey: nil)

picker.pushViewController(viewController, animated: false)
}
}

In the closer, the picker is unowned because it will be initialized and we don't want to create a memory leak (reference cycle). The self should be weak, because we use it and we don't want to create another memory leak.

In many closures, you have to use [weak self] to prevent memory leaks.

We will create an instance of the controller using its ID, CreatePost. Then, we will use it to pass the selected image to that instance, which will be responsible for sending the image to Firebase. Then, a nice transition effect is created and applied.

In the next section, we will discuss why creating a model of the data is important. What are the benefits of using an abstract model compared to storing the data directly on the server?

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.184.200