ActionSheetを使用してフィルターをカスタマイズする
Customizing our filter using ActionSheet
// // ContentView.swift // Instafilter-2 // // Created by Naoki Abe on 2020/08/23. // Copyright © 2020 Naoki Abe. All rights reserved. // import SwiftUI import CoreImage import CoreImage.CIFilterBuiltins struct ContentView: View { @State private var image: Image? @State private var filterIntensity = 0.5 @State private var showingImagePicker = false @State private var inputImage: UIImage? //画像を処理 //@State private var currentFilter = CIFilter.sepiaTone() @State private var currentFilter: CIFilter = CIFilter.sepiaTone() let context = CIContext() @State private var showingFilterSheet = false //画像を処理するメソッド func applyProcessing() { //currentFilter.intensity = Float(filterIntensity) //currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) let inputKeys = currentFilter.inputKeys if inputKeys.contains(kCIInputIntensityKey) { currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) } if inputKeys.contains(kCIInputRadiusKey) { currentFilter.setValue(filterIntensity * 200, forKey: kCIInputRadiusKey) } if inputKeys.contains(kCIInputScaleKey) { currentFilter.setValue(filterIntensity * 10, forKey: kCIInputScaleKey) } guard let outputImage = currentFilter.outputImage else { return } if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) { let uiImage = UIImage(cgImage: cgimg) image = Image(uiImage: uiImage) } } func setFilter(_ filter: CIFilter) { currentFilter = filter loadImage() } func loadImage() { guard let inputImage = inputImage else { return } let beginImage = CIImage(image: inputImage) currentFilter.setValue(beginImage, forKey: kCIInputImageKey) applyProcessing() } //------------------------------------------------------ var body: some View { //------------------------ let intensity = Binding<Double>( get: { self.filterIntensity }, set: { self.filterIntensity = $0 self.applyProcessing() } ) //------------------------ return NavigationView { VStack { ZStack { Rectangle() .fill(Color.secondary) // display the image if image != nil { image? .resizable() .scaledToFit() } else { Text("Tap to select a picture") .foregroundColor(.white) .font(.headline) } } .onTapGesture { // select an image self.showingImagePicker = true } HStack { Text("Intensity") Slider(value: intensity) }.padding(.vertical) HStack { Button("Change Filter") { // change filter self.showingFilterSheet = true } Spacer() Button("Save") { // save the picture } } } .padding([.horizontal, .bottom]) .navigationBarTitle("Instafilter") .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) { ImagePicker(image: self.$inputImage) } .actionSheet(isPresented: $showingFilterSheet) { // action sheet here ActionSheet(title: Text("Select a filter"), buttons: [ .default(Text("Crystallize")) { self.setFilter(CIFilter.crystallize()) }, .default(Text("Edges")) { self.setFilter(CIFilter.edges()) }, .default(Text("Gaussian Blur")) { self.setFilter(CIFilter.gaussianBlur()) }, .default(Text("Pixellate")) { self.setFilter(CIFilter.pixellate()) }, .default(Text("Sepia Tone")) { self.setFilter(CIFilter.sepiaTone()) }, .default(Text("Unsharp Mask")) { self.setFilter(CIFilter.unsharpMask()) }, .default(Text("Vignette")) { self.setFilter(CIFilter.vignette()) }, .cancel() ]) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
UIImageWriteToSavedPhotosAlbum()を使用してフィルターされた画像を保存する
このプロジェクトを完了するには、[保存]ボタンを有効にして、フィルター処理された写真をユーザーの写真ライブラリに保存し、ユーザーがさらに編集したり、共有したりできるようにします。
Privacy – Photo Library Additions Usage Description
We want to save the filtered photo.
Info.plistを開く
空白スペースを右クリック
[行を追加]を選択します
キー名として「プライバシー-フォトライブラリ追加機能の使用法の説明」を選択します。
「フィルタリングされた写真を保存したい」と入力します。値として。
import UIKit class ImageSaver: NSObject { var successHandler: (() -> Void)? var errorHandler: ((Error) -> Void)? func writeToPhotoAlbum(image: UIImage) { UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil) } @objc func saveError(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) { // save complete if let error = error { errorHandler?(error) } else { successHandler?() } } }
// // ContentView.swift // Instafilter-2 // // Created by Naoki Abe on 2020/08/23. // Copyright © 2020 Naoki Abe. All rights reserved. // import SwiftUI import CoreImage import CoreImage.CIFilterBuiltins struct ContentView: View { @State private var image: Image? @State private var filterIntensity = 0.5 @State private var showingImagePicker = false @State private var inputImage: UIImage? //画像を処理 //@State private var currentFilter = CIFilter.sepiaTone() @State private var currentFilter: CIFilter = CIFilter.sepiaTone() let context = CIContext() @State private var showingFilterSheet = false @State private var processedImage: UIImage? //画像を処理するメソッド func applyProcessing() { //currentFilter.intensity = Float(filterIntensity) //currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) let inputKeys = currentFilter.inputKeys if inputKeys.contains(kCIInputIntensityKey) { currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) } if inputKeys.contains(kCIInputRadiusKey) { currentFilter.setValue(filterIntensity * 200, forKey: kCIInputRadiusKey) } if inputKeys.contains(kCIInputScaleKey) { currentFilter.setValue(filterIntensity * 10, forKey: kCIInputScaleKey) } guard let outputImage = currentFilter.outputImage else { return } if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) { let uiImage = UIImage(cgImage: cgimg) image = Image(uiImage: uiImage) processedImage = uiImage } } func setFilter(_ filter: CIFilter) { currentFilter = filter loadImage() } func loadImage() { guard let inputImage = inputImage else { return } let beginImage = CIImage(image: inputImage) currentFilter.setValue(beginImage, forKey: kCIInputImageKey) applyProcessing() } //------------------------------------------------------ var body: some View { //------------------------ let intensity = Binding<Double>( get: { self.filterIntensity }, set: { self.filterIntensity = $0 self.applyProcessing() } ) //------------------------ return NavigationView { VStack { ZStack { Rectangle() .fill(Color.secondary) // display the image if image != nil { image? .resizable() .scaledToFit() } else { Text("Tap to select a picture") .foregroundColor(.white) .font(.headline) } } .onTapGesture { // select an image self.showingImagePicker = true } HStack { Text("Intensity") Slider(value: intensity) }.padding(.vertical) HStack { Button("Change Filter") { // change filter self.showingFilterSheet = true } Spacer() Button("Save") { guard let processedImage = self.processedImage else { return } let imageSaver = ImageSaver() imageSaver.successHandler = { print("Success!") } imageSaver.errorHandler = { print("Oops: \($0.localizedDescription)") } imageSaver.writeToPhotoAlbum(image: processedImage) } } } .padding([.horizontal, .bottom]) .navigationBarTitle("Instafilter") .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) { ImagePicker(image: self.$inputImage) } .actionSheet(isPresented: $showingFilterSheet) { // action sheet here ActionSheet(title: Text("Select a filter"), buttons: [ .default(Text("Crystallize")) { self.setFilter(CIFilter.crystallize()) }, .default(Text("Edges")) { self.setFilter(CIFilter.edges()) }, .default(Text("Gaussian Blur")) { self.setFilter(CIFilter.gaussianBlur()) }, .default(Text("Pixellate")) { self.setFilter(CIFilter.pixellate()) }, .default(Text("Sepia Tone")) { self.setFilter(CIFilter.sepiaTone()) }, .default(Text("Unsharp Mask")) { self.setFilter(CIFilter.unsharpMask()) }, .default(Text("Vignette")) { self.setFilter(CIFilter.vignette()) }, .cancel() ]) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }