[iOS] I tried to make a processing application like Instagram with Swift

I made a processing app with Swift!

The overview is as follows. 加工アプリ.gif

  1. Set the image

First create a project, move to Main.storyboard, then place the View, UIImageView, and buttons. First, View up, right, left 0, height 200. Next, I will put the UIImageView inside the View, but please do whatever you like with the layout. The buttons below are just placed in an atmosphere, so please customize them as you like. Also, the image is pasted on the UIImageView in an atmosphere, but it looks like a blank unless you set something, so it is easier to understand if you set something.

スクリーンショット 2020-11-03 10.19.18.jpg

Next, connect each part with IBOutlet.

Viewcontroler.swift


import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var photo: UIImageView!
    @IBOutlet weak var shareButton: UIButton!
    
    //Selected image
    var selectedImage: UIImage?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleSelectPhoto))
        photo.addGestureRecognizer(tapGesture)
        photo.isUserInteractionEnabled = true
    }

    @IBAction func shareButton_TouchUpInside(_ sender: Any) {
        view.endEditing(true)
    }
    
    @objc func handleSelectPhoto() {
        let pickerController = UIImagePickerController()
        pickerController.delegate = self
        pickerController.allowsEditing = true
        present(pickerController, animated: true, completion: nil)
    }
    
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let image = info[.editedImage] as? UIImage else { return }
        self.selectedImage = image
        photo.layer.masksToBounds = true
        photo.clipsToBounds = true
        photo.image = image
        dismiss(animated: true, completion: nil)
    }

}


I want to connect UIImageView to photo and use the image when selected

Viewcontroller.swift


    //Selected image
    var selectedImage: UIImage?

Is defined as. After that, tap detection is performed with viewDidLoad and UIImagePickerController () is called. Then, in the extension, the image selected by didFinishPickingMediaWithInfo is saved as a photo. Check out the code around here.

  1. Make a Filter screen

When you can set a photo, we will create a screen to select Filter. Create a new view controller and use it as a FilterViewController. Then set Present Mordally from Viewcontroller to FilterViewController. Please define "filter_segue" for the created segue. It will be used when transitioning later. Next, put the parts in the FilterViewController. As shown in the picture, first place a new View on top, and then place the Cancel button and Next button on that View, respectively. Then place the UIImageView under the View above. The image selected in the view controller is reflected in this. Finally, put the CollectionView under the UIImageView and keep it a little further away. Also in this Collection View Place UIImageView and select the image to process here. After putting it, look at the Scroll View of the Collection View. ・ Show Horizontal Indicator ・ Show Vertical Indicator Please uncheck the two. This allows you to express the horizontal slide UI that is often used in processing apps. スクリーンショット 2020-11-03 21.44.19.jpg

After placing the necessary parts, set the delegate and datasource individually. And since a new class is required for the cell of CollectionView, create "FilterCollectionViewCell.swift" that inherits UICollectionViewCell again. スクリーンショット 2020-11-03 20.04.15.jpg

FilterCollectionViewCell.swift


import UIKit

class FilterCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var filterPhoto: UIImageView!
}

Only the UICollectionView inside the CollectionView is fine here.

  1. Make a processing app![Screenshot 2020-11-03 20.38.24.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/848369/92166900- 05fa-13b4-e766-27ba042e6ad3.jpeg)

I will actually make it from here, but for the time being, I will put all the code first, so if you understand it, you can copy it all, but for those who do not understand, I will explain in order from the top.

FilterViewController.swift


import UIKit

protocol FilterViewControllerDelegate {
    func updatePhoto(image: UIImage)
}

class FilterViewController: UIViewController {
    
    @IBOutlet weak var collectionView: UICollectionView!
    @IBOutlet weak var filterPhoto: UIImageView!
    
    var delegate: FilterViewControllerDelegate?
    var selectedImage: UIImage!
    var CIFilterNames = [
        "CIPhotoEffectChrome",
        "CIPhotoEffectFade",
        "CIPhotoEffectInstant",
        "CIPhotoEffectNoir",
        "CIPhotoEffectProcess",
        "CIPhotoEffectTonal",
        "CIPhotoEffectTransfer",
        "CISepiaTone"
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        filterPhoto.image = selectedImage
        filterPhoto.contentMode = .scaleAspectFill
    }
    
    @IBAction func cancelBtn_TouchUpInside(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
    
    @IBAction func nextBtn_TouchUpInside(_ sender: Any) {
        dismiss(animated: true, completion: nil)
        delegate?.updatePhoto(image: self.filterPhoto.image!)
    }
    
    func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage {
        let scale = newWidth / image.size.width
        let newHeight = image.size.height * scale
        UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
        image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
    
}

extension FilterViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return CIFilterNames.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
        let context = CIContext(options: nil)
        let newImage = resizeImage(image: selectedImage, newWidth: 150)
        let ciImage = CIImage(image: newImage)
        let filter = CIFilter(name: CIFilterNames[indexPath.item])
        filter?.setValue(ciImage, forKey: kCIInputImageKey)
        if let filteredImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
            let result = context.createCGImage(filteredImage, from: filteredImage.extent)
            cell.filterPhoto.image = UIImage(cgImage: result!)
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let context = CIContext(options: nil)
        let ciImage = CIImage(image: selectedImage)
        let filter = CIFilter(name: CIFilterNames[indexPath.item])
        filter?.setValue(ciImage, forKey: kCIInputImageKey)
        if let filteredImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
            let result = context.createCGImage(filteredImage, from: filteredImage.extent)
            self.filterPhoto.image = UIImage(cgImage: result!)
        }

    }
}

FilterViewController Outlet connection I

FilterCollectionView.swift


@IBOutlet weak var collectionView: UICollectionView!
@IBOutlet weak var filterPhoto: UIImageView!
@IBAction func cancelBtn_TouchUpInside(_ sender: Any) {
        dismiss(animated: true, completion: nil)
}

@IBAction func nextBtn_TouchUpInside(_ sender: Any) {
        dismiss(animated: true, completion: nil)
        delegate?.updatePhoto(image: self.filterPhoto.image!)
}

I left it as. CancelButton returns to the previous screen, and NextButton reflects the image selected in the cell below to the view controller.

Next, define the properties used by the FilterViewController.

FilterCollectionView.swift


    var selectedImage: UIImage!
    var CIFilterNames = [
        "CIPhotoEffectChrome",
        "CIPhotoEffectFade",
        "CIPhotoEffectInstant",
        "CIPhotoEffectNoir",
        "CIPhotoEffectProcess",
        "CIPhotoEffectTonal",
        "CIPhotoEffectTransfer",
        "CISepiaTone"
    ]

You don't have to say selectedImage anymore, but CIFilterNames below should not be mistaken for any lowercase and uppercase letters. This time I will use only 8 types. The CI ~ described here is defined in Apple's Core Image Filter Reference guide. I want to use these in cells, so I'll put them in an array. It doesn't matter what is written separately, and 8 or more types are fine. Please add each as you like. Reference: Core Image Filter Reference

After adding, reflect the image selected in the previous (View Controller) with viewDidLoad so that all can be seen. Then, create a function for processing to be used in the cell.

FilterCollectionView.swift


    override func viewDidLoad() {
        super.viewDidLoad()
        filterPhoto.image = selectedImage
        filterPhoto.contentMode = .scaleAspectFill
    }

    func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage {
        let scale = newWidth / image.size.width
        let newHeight = image.size.height * scale
        UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
        image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }

numberOfItemsInSection counts the number of CIFilterNames. I'm counting 8 here. In cellForItemAt, set while using FilterCollectionViewCell. In newImage, the size is adjusted using the resizeImage function defined above, and a new Filter image is set for each cell. In ciImage, the image selected for filtering is set to CIImage. And ciImage is set as many as CIFilterNames in filter. After that, I created a new CGImage, made it a UIImage, and set it in the cell. didSelectItemAt is the same process.

FilterCollectionView.swift


extension FilterViewController: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return CIFilterNames.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
        let context = CIContext(options: nil)
        let newImage = resizeImage(image: selectedImage, newWidth: 150)
        let ciImage = CIImage(image: newImage)
        let filter = CIFilter(name: CIFilterNames[indexPath.item])
        filter?.setValue(ciImage, forKey: kCIInputImageKey)
        if let filteredImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
            let result = context.createCGImage(filteredImage, from: filteredImage.extent)
            cell.filterPhoto.image = UIImage(cgImage: result!)
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let context = CIContext(options: nil)
        let ciImage = CIImage(image: selectedImage)
        let filter = CIFilter(name: CIFilterNames[indexPath.item])
        filter?.setValue(ciImage, forKey: kCIInputImageKey)
        if let filteredImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
            let result = context.createCGImage(filteredImage, from: filteredImage.extent)
            self.filterPhoto.image = UIImage(cgImage: result!)
        }

    }
}

It seems like it's over when you reach this point, but as it is, the image selected by processing will not be reflected in the view controller. Therefore, I will define a new protocol.

FilterViewController.swift


protocol FilterViewControllerDelegate {
    func updatePhoto(image: UIImage)
}

Define delegate properties to use this protocol.

FilterViewController.swift


var delegate: FilterViewControllerDelegate?

Since I want to pass this delegate to the view controller for the selected image after processing, I will pass it through the delegate with the Next button.

FilterViewController.swift


@IBAction func nextBtn_TouchUpInside(_ sender: Any) {
        dismiss(animated: true, completion: nil)
        delegate?.updatePhoto(image: self.filterPhoto.image!)
}

Finally, add the following processing to ViewController.swift. This will reflect the selected image.

ViewController.swift


extension ViewController: FilterViewControllerDelegate {
    func updatePhoto(image: UIImage) {
        self.photo.image = image
    }
}

  1. At the end

This is my first post. The content of this processing app was created from the Udemy course below.

https://www.udemy.com/course/build-instagram-to-master-swift-and-firebase/

Overseas courses are difficult, but the quality is very high, so I highly recommend it. No one has posted how to make a processing application, so I introduced it. I think that if you modify it further, you can make a commercial level application. You should know that it's not that difficult. I hope it will be useful for beginners who are interested in how to make a processing application.

From now on, if there is content that no one has posted yet, I will introduce it at the same time as the output each time. Thank you.

Recommended Posts

[iOS] I tried to make a processing application like Instagram with Swift
I tried to make a machine learning application with Dash (+ Docker) part3 ~ Practice ~
I tried to modernize a Java EE application with OpenShift.
I tried to make an Android application with MVC now (Java)
I tried to make a group function (bulletin board) with Rails
I tried to make a simple face recognition Android application using OpenCV
I tried to make a Web API that connects to DB with Quarkus
I tried to build a Firebase application development environment with Docker in 2020
I tried to make a machine learning application with Dash (+ Docker) part2 ~ Basic way of writing Dash ~
I tried to make a talk application in Java using AI "A3RT"
I tried to break a block with java (1)
I tried to make a machine learning application with Dash (+ Docker) part1 ~ Environment construction and operation check ~
A story when I tried to make a video by linking Processing and Resolume
I tried to make a simple game with Javafx ① "Let's find happiness game" (unfinished)
[Android] I tried to make a material list screen with ListView + Bottom Sheet
I tried to clone a web application full of bugs with Spring Boot
I tried to make a login function in Java
I tried OCR processing a PDF file with Java
I tried to summarize the points to consider when acquiring location information with the iOS application ③
I tried to make a simple game with Javafx ① "Let's find happiness game" (unfinished version ②)
I tried to summarize the points to consider when acquiring location information with the iOS application ①
I tried to summarize the points to consider when acquiring location information with the iOS application ②
[Swift] I tried to implement Instagram profile-like UI with UICollectionView only with code without storyboard
[Azure] I tried to create a Java application for free ~ Connect with FTP ~ [Beginner]
I tried to make an introduction to PHP + MySQL with Docker
I tried to create a java8 development environment with Chocolatey
I tried to increase the processing speed with spiritual engineering
[Rails] I tried to create a mini app with FullCalendar
I want to make a list with kotlin and java!
I want to make a function with kotlin and java!
I tried using Hotwire to make Rails 6.1 scaffold a SPA
[Rails] I tried to implement batch processing with Rake task
I tried to implement a buggy web application in Kotlin
I tried to make a client of RESAS-API in Java
I tried to create a padrino development environment with Docker
I tried OCR processing a PDF file with Java part2
Make a Christmas tree with swift
I tried to summarize iOS 14 support
I tried to make the sample application into a microservice according to the idea of the book "Microservice Architecture".
I tried to interact with Java
I tried to implement Ajax processing of like function in Rails
I want to make a button with a line break with link_to [Note]
I tried running a letter of credit transaction application with Corda 1
I tried to write code like a type declaration in Ruby
Try to make a cross-platform application with JRuby (jar file generation)
[Unity] I tried to make a native plug-in UniNWPathMonitor using NWPathMonitor
I tried to build a simple application using Dockder + Rails Scaffold
[Java] I tried to make a maze by the digging method ♪
[Rails] Implementation of multi-layer category function using ancestry "I tried to make a window with Bootstrap 3"
I want to develop a web application!
Let's make a Christmas card with Processing!
I tried migrating Processing to VS Code
I tried to get started with WebAssembly
I tried playing with BottomNavigationView a little ①
I tried to use Selenium like JQuery
I tried to implement ModanShogi with Kinx
I tried to make a parent class of a value object in Ruby
I tried to make an automatic backup with pleasanter + PostgreSQL + SSL + docker
Java beginner tried to make a simple web application using Spring Boot
I made a virtual currency arbitrage bot and tried to make money
I tried to create a portfolio with AWS, Docker, CircleCI, Laravel [with reference link]