Build a Photo Effects App in Swift – XSTutorials

Build a Photo Effects App in Swift

In this tutorial, you’ll build a fully-functional iOS application with the following features:

  • Take a picture with the native iOS Camera
  • Pick an image from the device’s Photo Library
  • Show a horizontal grid of photo filters
  • Apply selected effects to an image
  • Share your filtered picture with the UIActivityViewController

Sounds good? Well, then let’s start creating your first Photo Editor app!

The Xcode project

Let’s open a blank Xcode project, select Single View App, give it a name like PhotoEffects, set the Organization identifier as you wish, choose Swift as language and click the Next button. Save this project on your Desktop or in any folder you wish.

TIP: When you create a new Xcode project, it’s best practice to give it a name without spaces and with capital letters to separate each word. In this case, our app’s name should be Photo Effects, but the project’s name must be PhotoEffects.

In order to make this app work on older devices, set the Deployment Target as 9.0 and check the Requires full screen option in the General tab in Xcode.

The General tab in Xode

The Info.plist file

This app will access the device’s Camera and Photo Library, so we must set the necessary Privacy Permissions in the Info.plist file. This will fire alerts in the application where users will have to allow such permissions i order to take pictures, pick images from the Library and share them with the UIActivityController.

So select the Info.plist file from the left menu, click any (+) button in any row and scroll down to select the needed Privacy options. You will also have to type a brief description of each option in the empty cell next to them. In the end, your Info.plist file should look like this:

The Info.plist file

The UI design

It’s always a good thing to start building an app from its design, so now select Main.storyboard from the list of files on the left panel.

NOTE: If you’re not familiar with the Xcode UI and commands, please read this article and then come back here.

I usually hate to use Autolayout, and you may follow me with this, it’ll give you more flexibility while arranging Views in the ViewControllers. So click the File inspector icon on the top-right corner of the Xcode window and deselect the Use Auto Layout option. Then click the Disable Trait Variations button from the popup.

Disable Auto Layout and Trait Variations

I also tend to design apps using the iPhone SE Controller in the Storyboard, that’s because iOS has got many different devices, and the one with the smallest screen size if the iPhone 5/SE. So click the View as: iPhone Xr button on the bottom of the screen and select the iPhone SE icon.

By clicking on the Object Library button, select an ImageView and drag it to the top of the Controller. Then pick up a Label and two Buttons. You can style those Views as you wish by selecting them and using the Attributes inspector. Here’s how the ViewController should look like at the end:

The ViewController design

Now let’s drag a new ViewController from the Object Library to the Storyboard, place it next to the other one. This Controller will be the Filters Editor screen, we’ll see it later how to write the necessary code for both Controllers.

Drag a ViewController into the Storyboard

In order to build this screen, perform the following actions:

  • Drag a Button to the top-left corner of the empty Controller, double-click on it and type “Back” – this will be the button that will dismiss this Controller and go back to the home screen. Set its size as 44×44 and align it to the top-left from the Size inspector.
  • Drag a Label, center it to the top of the Controller and assign it the “EDITOR” title.
  • Drag another Button to the top-right corner and assign it a title like “Share”. Set its size as 44×44 and align it to the top-right from the Size inspector.
  • Drag an ImageView to the center of the Controller, resize it to 320×368 and enable all its Autoresizing margins from the Size inspector.
  • Add another ImageView over that one, it must have the same size and position as the previous one.
  • Lastly, place a ScrollView on the bottom of the Controller, set its size as 320×80 and attach it to the center-bottom from the Size inspector.

Your new ViewController design should look like this:

The Filters Editor controller’s design

Open Assistant editor and connect Views and Actions

Before jumping into the code, we have to connect our Views (IBOutlets) and IBActions from the ViewControllers in the Storyboard to their relative Swift files.

First of all, click File -> New -> File..., select Cocoa Touch Class from the iOS tab of the popup window that will show up and click the Next button.

Create a new File
Select the iOS Cocoa Touch Class

Type “FiltersEditor” as Class name, make sure it’s a Subclass of UIViewController, leave the XIB option unchecked and click the Next button.

create the FiltersEditor.swift file

You’ve just created the FiltersEditor.swift file. One important thing you have to do is to assign its Class name and Storyboard ID in the Identity inspector, so open it, type “FiltersEditor” in the Class field and hit Enter. Type it again in the Storyboard ID‘s filed and hit Enter.

Assign Class and Storyboard ID names

Now select the first ViewController and click the Assistant editor button in Xcode. In this way you’ll split the Editor area into 2 sections, the left one will still show the Storyboard, the right one will display the ViewController.swift file.

Start by adding these two Delegates to the Class: UIImagePickerControllerDelegate, UINavigationControllerDelegate. The code will look like this:

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

Declare a variable right below the Class declaration:

var pickedImage = UIImage()

Now, inside the Class – and outside the viewDidLoad() function – connect the first button to your Swift file and name its IBAction as “takePicButt”, then hit Enter. Do the same for the second button, the one you’ve titled as “PICK FROM LIBRARY”, and name its Action as “libraryButt”.

Declare IBActions and variables

Now switch to the FiltersEditor Controller in the Storyboard. If the Assistant editor‘s top menu is set to Automatic, you should see the FiltersEditor.swift file on the right side of the Editor’s area.

Here you have to declare the originalImage, imageToFilter ImageViews, and the filtersScrollView ScrollView as IBOutlets. Then create an UIImage variable:

 var passedImage = UIImage()
Declare variables and IBOutlets

Connect the Share button to your Swift file and name its Action as “sharePictureButt”, then hit Enter. Then connect the Back button and name its Action as “backButt”.

Connect IBActions for the Buttons

Finally, you’re ready to write some code that will make your app alive! I guess it was easy to follow this tutorial so far, but don’t get scared, coding will be not so hard, I promise 😉

Write the code

You can expand the Standard editor and select the ViewController.swift file, we don’t need the Storyboard anymore. Scroll down to the takePicButt() function and paste this piece of code inside it:

// Instantiate the Camera controller
if UIImagePickerController.isSourceTypeAvailable(.camera) {
    let imagePicker = UIImagePickerController()
    imagePicker.delegate = self
    imagePicker.sourceType = .camera;
    imagePicker.allowsEditing = false
    present(imagePicker, animated: true, completion: nil)
}

Paste the code below into the libraryButt() function:

// Instantiate the Photo Library controller
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
    let imagePicker = UIImagePickerController()
    imagePicker.delegate = self
   imagePicker.sourceType = .photoLibrary;
   imagePicker.allowsEditing = false
   present(imagePicker, animated: true, completion: nil)
}

Then we need to call the ImagePicker’s delegate function that handles a taken UIImage either form the native Camera or Photo Library. the delegate function we’ll call is the didFinishPickingMediaWithInfo one. So, right below the closure of the libraryButt() function, start typing imagepickerdidfinish and wait for the dropdown list that will show up, then select the suggest row. This is how Xcode will create our needed function:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        (code)
}

Below you can get the code to paste into that delegate method:

if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
    pickedImage = image
}
dismiss(animated: true, completion: nil)
        
// Prepare the Filters Editor controller
let aVC = storyboard?.instantiateViewController(withIdentifier: "FiltersEditor") as! FiltersEditor

// Pass the pickedImage UIImage to the next controller
aVC.passedImage = pickedImage
        
// Add a transition style between the 2 controllers
aVC.modalTransitionStyle = .crossDissolve
        
// Finally show the controller
present(aVC, animated: true, completion: nil)

You are done with the ViewController.swift file’s code, it was easy, right?

Select the FiltersEditor.swift file from the left menu in XCode and start by importing Apple’s CoreImage framework on the top of the file, right below import UIKit:

import CoreImage

Then paste this array right below your import statements:

var CIFilterNames = [
    "CIPhotoEffectChrome",
    "CIPhotoEffectFade",
    "CIPhotoEffectInstant",
    "CIPhotoEffectNoir",
    "CIPhotoEffectProcess",
    "CIPhotoEffectTonal",
    "CIPhotoEffectTransfer",
    "CISepiaTone"
]

The above array is a list of Core Image Filter names – you can read the Core Image Filter Reference here. The app will use this array to process various effects on your picked picture.

Paste this line inside the viewDidLoad() function in order to assign your picked image to the originalImage ImageView:

originalImage.image = passedImage

Below that line, we need to declare a few CGFloat variables to adjust the Buttons that will filter your photo:

var X: CGFloat = 5
let Y: CGFloat = 5
let W: CGFloat = 70
let H: CGFloat = 70
let G: CGFloat = 5
var counter = 0

The counter is an Int variable to handle tags.

It’s time to write the for loop that will create the filter buttons and assign the Core Image filters to them:

for i in 0..<CIFilterNames.count {
    counter = i
    
    // Initialize a Filer Button
    let filterButton = UIButton(type: .custom)
    filterButton.frame = CGRect(x: X, y: Y, width: W, height: H)
    filterButton.tag = i
    filterButton.addTarget(self, action: #selector(filterButtonTapped(_:)), for: .touchUpInside)
    filterButton.layer.cornerRadius = 6
    filterButton.clipsToBounds = true
    filterButton.imageView?.contentMode = .scaleAspectFill
            
            
    // Create a filter for each button, based on their position
    let ciContext = CIContext(options: nil)
    let coreImage = CIImage(image: originalImage.image!)
    let filter = CIFilter(name: "(CIFilterNames[i])" )
    filter!.setDefaults()
    filter!.setValue(coreImage, forKey: kCIInputImageKey)
    let filteredImageData = filter!.value(forKey: kCIOutputImageKey) as! CIImage
    let filteredImageRef = ciContext.createCGImage(filteredImageData, from: filteredImageData.extent)
            
    // Adjust fitered image's orientation (in case you've taken a picture with the Camera)
    let originalOrientation = originalImage.image?.imageOrientation
    let originalScale = originalImage.image?.scale
            
    // Assign filtered image to the button
    filterButton.setImage(UIImage(cgImage: filteredImageRef!, scale: originalScale!, orientation: originalOrientation!), for: .normal)
                    
    // Add Buttons in the filtersScrollView
    X += W + G
    filtersScrollView.addSubview(filterButton)
            
} // // ./ For

When you’ll run your app, that piece of code will create custom Buttons that will filter your picked image and display it inside the same Buttons, based on the different effects called by the for loop.

The last thing to do in the viewDidLoad() function is to set the content size of the filtersScrollView based on the number of generated Buttons:

filtersScrollView.contentSize = CGSize(width: W * CGFloat(counter+2), height: H)

We have assigned a target to the filterButton instance contained in the for loop, so we have to call it by creating another function outside the viewDidLoad() one:

@objc func filterButtonTapped(_ sender: UIButton) {
        let button = sender as UIButton
        
        // Apply the filteres image of the selected button to the imageToFilter UIImage instance
        imageToFilter.image = button.imageView!.image!
}

The above method will get the Button’s image and simply assign it to the imageToFilter‘s ImageView.

We’re almost done, what’s left now is the function that allows you to share your processed picture, so paste this code inside the sharePictureButt() method:

// This is the message that will show up while sharing the app on Twitter
let messageStr  = "Sharing my cool picure with #Photo Effects"
        
// This is the image that will get attached to the shating message
let img = imageToFilter.image!
        
// Prepare the iOS ActivityController
let shareItems = [messageStr, img] as [Any]
let vc = UIActivityViewController(activityItems: shareItems, applicationActivities: nil)
vc.excludedActivityTypes = [.print, .postToWeibo, .copyToPasteboard, .addToReadingList, .postToVimeo]
        
if UIDevice.current.userInterfaceIdiom == .pad {
    // iPad
    vc.modalPresentationStyle = .popover
    vc.popoverPresentationController?.sourceView = view
    vc.popoverPresentationController!.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
    vc.popoverPresentationController?.permittedArrowDirections = []
    present(vc, animated: true, completion: nil)
} else {
    // iPhone
    present(vc, animated: true, completion: nil)
}

This function will open the native iOS UIActivityController and share your edited photo along with a custom message to all the apps that will show up in the Controller.

One step away from running and testing our awesome app, just add the following line into the backButt()‘s IBAction:

dismiss(animated: true, completion: nil)

This code will dismiss the FiltersEditor controller and show the initial screen again.

Ready? Plug your device into the USB port of your Mac with the proper cable – you cannot test this app on the Simulator since it doesn’t support the camera – and hit the Run button in Xcode!

Here are some screenshots about how the application looks like:

Allow the Camera permission
Filter and image
Instant filter by Core Image
Gallery of photo effects
UIActivityController
Share your picture on Twitter

Conclusion

That’s all for this tutorial, you have learned how to build a photo filter application with the aid of Core Image in Xcode.

Hope you enjoyed this article, feel free to post comments about it. You can also download the full Xcode project of this tutorial, just click the link below:

Download the Xcode project

Buy me a coffee - XScoder - thanks for your support
Your support will be highly appreciated 😉