아이폰에 기본으로있는 포토 앱을 실행하고 편집을 눌러서 사진을 편집할수 있는 extensions 앱을 만들어본다.


프로젝트를 생성한 후

File -> New -> Target 진입

왼쪽에 IOS > Application Extension 을 클릭하고 오른쪽에 Photo Editing Extension을 선택한다. 

Product name을 입력하고 finish버튼을 클릭하면 스킴을 활성화할거냐고 팝업이 나오는데 Activate를 클릭한다.


새로추가한 extensions 안에는 controller.swift 파일과 storyboard 파일 info.plist 파일이 있다.



info.plist파일을 우클릭하고 open As -> source Code 로 들어가서 아래 코드를 입력한다.


<key>NSExtension</key>

    <dict>

        <key>NSExtensionAttributes</key>

        <dict>

            <key>PHSupportedMediaTypes</key>

            <array>

                <string>Image</string>

            </array>

        </dict>

        <key>NSExtensionMainStoryboard</key>

        <string>MainInterface</string>

        <key>NSExtensionPointIdentifier</key>

        <string>com.apple.photo-editing</string>

    </dict>



storyboard파일에서 default로 설정되어있는 Hello World 를 삭제하고

편집할 사진을 보여줄 imageView와 버튼들을 입력하고 controller 파일에 연동한다.




마지막으로 아래 코드와 같이 controller파일을 수정하여 photo앱에서 사진을 불러오고 버튼액션으로 편집하고 저장하여 내보도록 한다. 


import UIKit

import Photos

import PhotosUI


class PhotoEditingViewController: UIViewController, PHContentEditingController {


    @IBOutlet weak var imageView: UIImageView!

    

    var input: PHContentEditingInput?

    

    var displayedImage : UIImage?

    var imageOrientation : Int32?

    var currentFilter = "CIColorInvert"

    

    

    

    func performFilter(inputImage: UIImage, orientation: Int32?) -> UIImage? {

        var resultImage : UIImage?

        var cimage: CIImage

        cimage = CIImage(image: inputImage)!

        if orientation != nil {

            cimage = cimage.imageByApplyingOrientation(orientation!)

        }

        if let filter = CIFilter(name: currentFilter) {

            filter.setDefaults()

            filter.setValue(cimage, forKey: "inputImage")

            

            switch currentFilter {

            case "CISepiaTone", "CIEdges" :

                filter.setValue(0.8, forKey: "inputIntensity")

            case "CIMotionBlur" :

                filter.setValue(25.00, forKey: "inputRadius")

                filter.setValue(0.00, forKey: "inputAngle")

            default :

                break

            }

            let ciFilteredImage = filter.outputImage

            let context = CIContext(options:nil)

            let cgImage = context.createCGImage(ciFilteredImage!, fromRect: ciFilteredImage!.extent)

            resultImage = UIImage(CGImage: cgImage)

        }

        return resultImage

    }

    

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }

    

    

    


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    // MARK: - PHContentEditingController


    func canHandleAdjustmentData(adjustmentData: PHAdjustmentData?) -> Bool {

        // Inspect the adjustmentData to determine whether your extension can work with past edits.

        // (Typically, you use its formatIdentifier and formatVersion properties to do this.)

        return false

    }


    func startContentEditingWithInput(contentEditingInput: PHContentEditingInput?, placeholderImage: UIImage) {

        // Present content for editing, and keep the contentEditingInput for use when closing the edit session.

        // If you returned true from canHandleAdjustmentData:, contentEditingInput has the original image and adjustment data.

        // If you returned false, the contentEditingInput has past edits "baked in".

        input = contentEditingInput

        

        if input != nil {

            displayedImage = input!.displaySizeImage

            imageOrientation = input!.fullSizeImageOrientation

            imageView.image = displayedImage

        }

        print("photo loading success")

    }

    

    @IBAction func sepiaSelected(sender: AnyObject) {

        print("sepiaSeleted")

        currentFilter = "CISepiaTone"

        if displayedImage != nil {

            imageView.image = performFilter(displayedImage!, orientation: nil)

        }

    }

    

    @IBAction func monoSelected(sender: AnyObject) {

        print("monoSelected")

        currentFilter = "CIPhotoEffectMono"

        if displayedImage != nil {

            imageView.image = performFilter(displayedImage!, orientation: nil)

        }

    }

    

    @IBAction func invertSelected(sender: AnyObject) {

        print("invertSelected")

        currentFilter = "CIColorInvert"

        if displayedImage != nil {

            imageView.image = performFilter(displayedImage!, orientation: nil)

        }

    }


    func finishContentEditingWithCompletionHandler(completionHandler: ((PHContentEditingOutput!) -> Void)!) {

        // Update UI to reflect that editing has finished and output is being rendered.

        

        // Render and provide output on a background queue.

        dispatch_async(dispatch_get_global_queue(CLong(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0)) {

            // Create editing output from the editing input.

            let output = PHContentEditingOutput(contentEditingInput: self.input!)

            

            let url = self.input?.fullSizeImageURL

            if let imageUrl = url {

                let fullImage = UIImage(contentsOfFile: imageUrl.path!)

                let resultImage = self.performFilter(fullImage!, orientation: self.imageOrientation)

                

                if let renderedJPEGData = UIImageJPEGRepresentation(resultImage!, 0.9) {

                    renderedJPEGData.writeToURL(output.renderedContentURL, atomically: true)

                }

                let archivedData = NSKeyedArchiver.archivedDataWithRootObject(self.currentFilter)

                let adjustmentData = PHAdjustmentData(formatIdentifier: "com.ebookfrenzy.photoext", formatVersion: "1.0", data: archivedData)

                output.adjustmentData = adjustmentData

            }

            

            completionHandler?(output)

            

            // Clean up temporary files, etc.

        }

    }


    var shouldShowCancelConfirmation: Bool {

        // Determines whether a confirmation to discard changes should be shown to the user on cancel.

        // (Typically, this should be "true" if there are any unsaved changes.)

        return false

    }


    func cancelContentEditing() {

        // Clean up temporary files, etc.

        // May be called after finishContentEditingWithCompletionHandler: while you prepare output.

    }


}








위의 
코드는  "핵심만 골라 배우는 ios 9 프로그래밍 (닐스미스 지음/ 황반석 옮김)" 에 있는 예제를 참고하여 작성하였습니다.

+ Recent posts