Indonesian (Bahasa Indonesia) translation by Johari Triana (you can also view the original English article)
Tutorial ini akan mengajarkan Anda bagaimana menggunakan GPUImage untuk menerapkan filter gambar secara real-time sebagai camera feed ditampilkan. Sepanjang jalan, Anda akan belajar bagaimana untuk secara otomatis mengisi gambar dalam controller carausel dan bagaimana untuk mengubah ukuran gambar dengan UIImage + kategori.
Overview projek aplikasi
Prasyarat tutorial
Tutorial ini didasarkan pada sebelumnya post berjudul "Build a Photo App with GPUImage". Pelajaran sebelumnya menunjukkan bagaimana menggunakan UIImagePickerController
untuk memilih foto dari perangkat foto album atau kamera, bagaimana untuk menambahkan GPUImage
perpustakaan ke proyek Anda, dan bagaimana menggunakan GPUImageFilter
class untuk meningkatkan kamera frame. Jika Anda sudah akrab dengan UIImagePickerController
dan dapat mengetahui bagaimana menambahkan GPUImage
untuk proyek Anda sendiri, Anda harus mampu mengambil dari mana tutorial terakhir Tinggalkan saja.
Langkah 1: Impor iCarousel
Proyek ini akan banyak menggunakan sebuah proyek open source yang disebut iCarousel untuk menambahkan style tampilan foto yang dipilih.
Untuk memasukkan iCarousel dalam proyek Anda, pergi ke halaman GitHub resmi dan men-download kode sumber sebagai zip file. Mengambil kode dari ZIP file dan kemudian drag-and-drop folder yang berjudul "iCarousel" ke Xcode proyek Navigator. Folder ini harus berisi iCarousel.h dan iCarousel.m. Pastikan untuk memilih "Create groups for any added folders" dan periksa kotak di samping "Copy items into destination group's folder (if needed)" dan kotak di sebelah nama sasaran proyek Anda di daerah "Add to targets".
Selanjutnya pergi ke ViewController.m dan menambahkan statement import untuk iCarousel:
#import "ViewController.h" #import "GPUImage.h" #import "iCarousel/iCarousel.h"
Langkah 2: Mengimpor UIImage + Kategori
Sebelum kami menampilkan gambar kami dengan iCarousel, kita akan perlu menurunkan skala ke ukurang yang dibolehkan. daripada menulis semua kode untuk melakukan hal ini dengan tangan, kami akan membuat menggunakan UIImage + kategori proyek, yang menyediakan fungsi dasar untuk mengubah ukuran gambar serta beberapa trik manipulasi gambar lainnya.
Men-download kode UIImage + kategori
dari GitHub dan kemudian membuat grup baru dengan nama yang sama dalam Xcode. Tarik file implementasi dan header untuk UIImage + Alpha
, UIImage + Resize
dan UIImage + RoundedCorner
ke proyek Anda. Pastikan untuk memilih "Create groups for any added folders" dan periksa kotak di samping "Copy items into destination group's folder (if needed)" dan kotak di sebelah nama sasaran proyek Anda di daerah "Add to targets".
Dalam ViewController.m file, impor Kategori gambar dengan baris kode berikut:
#import "ViewController.h" #import "GPUImage.h" #import "iCarousel.h" #import "UIImage+Resize.h"
Langkah 3: Menambahkan iCarousel View di IB
Dengan kode iCarousel diimpor ke proyek kami, beralih ke file MainStoryboard.storyboard untuk pengerjaan ulang antarmuka kami.
Pertama, pilih UIImageView
saat ini yang terhubung ke selectedImageView
IBOutlet
dan menghapusnya. Beralih kembali ke ViewController.m dan memodifikasi kode proyek seperti berikut:
@property(nonatomic, weak) IBOutlet iCarousel *photoCarousel; @property(nonatomic, weak) IBOutlet UIBarButtonItem *filterButton; @property(nonatomic, weak) IBOutlet UIBarButtonItem *saveButton; - (IBAction)photoFromAlbum; - (IBAction)photoFromCamera; - (IBAction)saveImageToAlbum; - (IBAction)applyImageFilter:(id)sender; @end @implementation ViewController @synthesize photoCarousel, filterButton, saveButton;
baris 1 di atas, ganti selectedImageView
outlet dengan sebuah outlet iCarousel
yang disebut photoCarousel
. Swap keluar variabel dalam pernyataan synthesize di baris 14 di atas juga.
Kembali ke interface Builder dan menyeret UIView
baru ke tampilan controller. Dengan UIView
baru yang dipilih, pergi ke tab "Indentitiy Inspector" dalam panel Utilities dan mengatur nilai kolom "Class" ke "iCarousel". Ini memberitahu Interface Builder bahwa UIView
yang kami menambahkan proyek harus instantiated sebagai instance class iCarousel
.



Sekarang membuat hubungan antara outlet photoCarousel
hanya deklarasi dan UIView
yang baru saja ditambahkan sebagai subview.



Kita perlu untuk menetapkan sumber data dan delegasi untuk photoCarousel
juga, dan kita dapat mencapai ini dari dalam interface Builder. Pertama, pergi ke ViewController.h dan menyatakan bahwa view controller ini akan sesuai dengan protokol yang sesuai:
#import <UIKit/UIKit.h> #import "iCarousel/iCarousel.h" @interface ViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIActionSheetDelegate, iCarouselDataSource, iCarouselDelegate>
Baris 2 kita mengimpor iCarousel, dan baris 4 kami kemudian menyatakan kesesuaian untuk delegasi dan sumber data.
Kembali ke storyboard file, sekarang Anda bisa memetakan sumber data dan delegasi ke view controller.



Sebelum pindah, silakan dan mengubah warna latar belakang pemandangan iCarousel
ke hitam.
Oke, hanya satu hal lagi. Kami ingin view iCarousel untuk muncul di bawah UIToolbar
dalam hirarki view. Anda dapat melakukan ini secara visual dengan hanya menyeret mereka ke urutan yang benar di interface Builder:

Perhatikan bagaimana tampilan iCarousel sekarang muncul sebelum Toolbar.
Menyimpan pekerjaan Anda dalam Interface Builder.
Langkah 4: Mengimplementasikan protokol iCarousel
iCarousel menggunakan design pattern yang mirip dengan UITableView
bahwa sumber data yang digunakan untuk pakan masukan ke dalam kontrol dan delegasi digunakan untuk menangani interaksi dengan kontrol.
Untuk proyek kami, sumber data yang akan NSMutableArray
sederhana yang disebut "displayImages". Menambahkan ini ke ekstensi class dalam ViewController.m sekarang:
#import "UIImage+Resize.h" @interface ViewController () { NSMutableArray *displayImages; } @property(nonatomic, weak) IBOutlet iCarousel *photoCarousel;
Selanjutnya, kami ingin mengalokasikan memori untuk array dalam c;ass initializer Ruangan Khusus. Dalam kasus kami, voew controller akan instantiated dari Storyboard, sehingga initialize yang tepat initWithCoder
:. Namun, jika class instantiated pemrograman dari XIB, initializer yang tepat akan initWithNibName:bundle
:. Untuk mengakomodasi baik style inisialisasi, kita akan menulis initializer kita sendiri kustom dan menyebutnya dari keduanya, seperti:
- (void)customSetup { displayImages = [[NSMutableArray alloc] init]; } - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { [self customSetup]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { if ((self = [super initWithCoder:aDecoder])) { [self customSetup]; } return self; }
Sekarang kita dapat implementasi sumber data dan mendelegasikan. Mulailah dengan data sumber metode numberOfItemsInCarousel
:, seperti:
#pragma mark #pragma mark iCarousel DataSource/Delegate/Custom - (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel { return [displayImages count]; }
Ini akan memberitahu iCarousel berapa banyak gambar untuk menampilkan dengan melihat jumlah gambar yang disimpan dalam array sumber data.
Selanjutnya, menulis metode yang benar-benar akan menghasilkan view untuk setiap gambar yang ditampilkan di carousel:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view { // Create new view if no view is available for recycling if (view == nil) { view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter; } ((UIImageView *)view).image = [displayImages objectAtIndex:index]; return view; }
Ini adalah awal yang baik, tapi ada satu masalah yang sangat signifikan di atas: gambar harus diperkecil sebelum dipasok ke iCarousel. Tambahkan baris berikut kode untuk memperbarui metode:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view { // Create new view if no view is available for recycling if (view == nil) { view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300.0f, 300.0f)]; view.contentMode = UIViewContentModeCenter; } // Intelligently scale down to a max of 250px in width or height UIImage *originalImage = [displayImages objectAtIndex:index]; CGSize maxSize = CGSizeMake(250.0f, 250.0f); CGSize targetSize; // If image is landscape, set width to 250px and dynamically figure out height if(originalImage.size.width >= originalImage.size.height) { float newHeightMultiplier = maxSize.width / originalImage.size.width; targetSize = CGSizeMake(maxSize.width, round(originalImage.size.height * newHeightMultiplier)); } // If image is portrait, set height to 250px and dynamically figure out width else { float newWidthMultiplier = maxSize.height / originalImage.size.height; targetSize = CGSizeMake( round(newWidthMultiplier * originalImage.size.width), maxSize.height ); } // Resize the source image down to fit nicely in iCarousel ((UIImageView *)view).image = [[displayImages objectAtIndex:index] resizedImage:targetSize interpolationQuality:kCGInterpolationHigh]; return view; }
Di atas, kita menetapkan ukuran maksimum 250px antara lebar atau tinggi gambar, dan kemudian kita skala atribut berlawanan untuk mencocokkan. Ini kendala proporsi gambar dan terlihat jauh lebih baik daripada hanya skala ke 250px oleh 250px persegi.
Dua metode di atas adalah semua kebutuhan iCarousel untuk mulai menampilkan gambar.
Dengan metode sumber delegasi dan data yang dikonfigurasi, sekarang adalah waktu yang baik untuk setup iCarousel
objek dalam metode viewDidLoad juga:
- (void)viewDidLoad { [super viewDidLoad]; // iCarousel Configuration self.photoCarousel.type = iCarouselTypeCoverFlow2; self.photoCarousel.bounces = NO; }
Dengan hanya beberapa tweak, proyek akan mampu menampilkan gambar dalam carousel!
Langkah 5: Beralih ke Model Data baru
Sebelumnya dalam tutorial ini, kita digantikan selectedImageView
properti dengan properti photoCarousel
, diperbarui interface Storyboard untuk mencocokkan, dan menciptakan NSMutableArray
untuk bertindak sebagai iCarousel data model. Namun, ada beberapa metode di ViewController.m yang masih menggunakan model data lama yang akan mencegah proyek kompilasi, jadi mari kita memperbaiki mereka sekarang. Pembaruan metode saveImageToAlbum
seperti:
- (IBAction)saveImageToAlbum { UIImage *selectedImage = [displayImages objectAtIndex:self.photoCarousel.currentItemIndex]; UIImageWriteToSavedPhotosAlbum(selectedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); }
Baris 3 memilih UIImage
dari data model yang cocok dengan indeks iCarousel saat ini. Baris 4 melakukan yang sebenarnya disk menulis dengan gambar.
Selanjutnya, pergi ke UIImagePickerController
mendelegasikan metode dan memodifikasi kode:
- (void)imagePickerController:(UIImagePickerController *)photoPicker didFinishPickingMediaWithInfo:(NSDictionary *)info { self.saveButton.enabled = YES; self.filterButton.enabled = YES; [displayImages addObject:[info valueForKey:UIImagePickerControllerOriginalImage]]; [self.photoCarousel reloadData]; [photoPicker dismissViewControllerAnimated:YES completion:NULL]; }
Pada baris 6 di atas, kita menambahkan foto yang dipilih ke model baru dan baris 8 kita memaksa merefresh carousel.
Hanya satu perubahan lagi. Pergi ke clickedButtonAtIndex
action sheet: metode dan memodifikasi kode sebagai berikut:
#pragma mark - #pragma mark UIActionSheetDelegate - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex == actionSheet.cancelButtonIndex) { return; } GPUImageFilter *selectedFilter; switch (buttonIndex) { case 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; break; case 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; break; case 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; break; case 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; break; case 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; break; case 5: selectedFilter = [[GPUImageToonFilter alloc] init]; break; case 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; break; case 7: selectedFilter = [[GPUImageFilter alloc] init]; break; default: break; } UIImage *filteredImage = [selectedFilter imageByFilteringImage:[displayImages objectAtIndex:self.photoCarousel.currentItemIndex]]; [displayImages replaceObjectAtIndex:self.photoCarousel.currentItemIndex withObject:filteredImage]; [self.photoCarousel reloadData]; }
Tiga baris akhir dari metode ini akan menyaring data model gambar yang sesuai dengan indeks caraousel saat ini, menggantikan tampilan carousel dengan gambar dan kemudian merefresh caraousel.
Jika semua berjalan baik, sekarang Anda dapat mengkompilasi dan menjalankan proyek! Melakukan hal ini akan memungkinkan Anda untuk melihat gambar Anda dalam carousel bukan hanya dalam image view.
Langkah 6: Menambahkan Gesture untuk menghapus gambar
App terlihat baik sejauh ini, tapi akan menyenangkan jika pengguna dapat menghapus foto dari carousel sesudah menambahkannya ke layar. Tidak ada masalah! Kita dapat memilih semua subclass UIGestureRecognizer
membuat ini terjadi. Untuk tutorial ini, aku telah memilih untuk menggunakan two-finger double-tap. gesture ini mungkin tidak segera intuitif, tetapi sangat mudah untuk melakukan dan kompleksitas ditambahkan akan membantu mencegah penghapusan gambar secara tidak sengaja.
Dalam ViewController.m file, pergi ke carausel: viewForItemAtIndex:reusingView
: metode dan menambahkan baris berikut sebelum akhir metode:
// Resize the source image down to fit nicely in iCarousel ((UIImageView *)view).image = [[displayImages objectAtIndex:index] resizedImage:targetSize interpolationQuality:kCGInterpolationHigh]; // Two finger double-tap will delete an image UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(removeImageFromCarousel:)]; gesture.numberOfTouchesRequired = 2; gesture.numberOfTapsRequired = 2; view.gestureRecognizers = [NSArray arrayWithObject:gesture]; return view;
Baris 4-8 deklarasi objek UITapGestureRecognizer
baru, menetapkan jumlah sentuhan (finger) diperlukan untuk memicu gerakan ke 2, dan menetapkan jumlah keran yang diperlukan untuk 2 juga. Akhirnya, sebelum melewati view ke obyek iCarousel, kita mengatur properti gestureRecognizers
dengan recognizer baru dibentuk.
Catatan bahwa bila dipicu, recognizer gerakan ini akan menjalankan removeImageFromCarousel
selector:. Mari kita menerapkan berikutnya:
- (void)removeImageFromCarousel:(UIGestureRecognizer *)gesture { [gesture removeTarget:self action:@selector(removeImageFromCarousel:)]; [displayImages removeObjectAtIndex:self.photoCarousel.currentItemIndex]; [self.photoCarousel reloadData]; }
Baris 3 menghilangkan gesture dari target saat ini untuk mencegah beberapa gerakan yang dipicu ketika memproses. Sisa dua baris yang tidak baru saat ini.
Build dan menjalankan aplikasi lagi. Anda sekarang harus mampu secara dinamis menghapus item dari carousel!
Step 7: Membuat MTCameraViewController
Sisanya dari tutorial ini akan fokus pada menggunakan GPUImageStillCamera
untuk membangun kustom kamera picker control yang dapat menerapkan filter untuk video yang masuk secara real time. GPUImageStillCamera
bekerja sama dengan class disebut GPUImageView
. Frame kamera yang dihasilkan oleh GPUImageStillCamera
dikirim ke objek GPUImageView
yang diberikan untuk ditampilkan kepada pengguna. Semua ini dicapai dengan fungsi yang mendasari yang disediakan oleh framework AVFoundation
, yang menyediakan akses pragmatis ke kamera frame data.
Karena GPUImageView
child class dari UIView
, kita dapat menanamkan tampilan seluruh kamera ke dalam class UIViewController
kustom kami sendiri.
Menambahkan sebuah subclass UIViewController
baru untuk proyek dengan mengklik kanan "PhotoFX" di navigasi proyek, dan kemudian memilih New File > Objective-C class. Nama claass "MTCameraViewController" dan masukkan "UIViewController" di bidang "subclass".



Klik "Next" dan kemudian "Create" untuk menyelesaikan proses.
Pergi ke MTCameraViewController.m file dan impor GPUImage:
#import "MTCameraViewController.h" #import "GPUImage.h"
Selanjutnya buat ekstensi class dengan diperlukan GPUImage data anggota:
@interface MTCameraViewController () <UIActionSheetDelegate> { GPUImageStillCamera *stillCamera; GPUImageFilter *filter; } @end
Akhirnya, pergi ke viewDidLoad
: metode dan menambahkan kode untuk memulai penangkapan kamera:
- (void)viewDidLoad { [super viewDidLoad]; // Setup initial camera filter filter = [[GPUImageFilter alloc] init]; [filter prepareForImageCapture]; GPUImageView *filterView = (GPUImageView *)self.view; [filter addTarget:filterView]; // Create custom GPUImage camera stillCamera = [[GPUImageStillCamera alloc] init]; stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait; [stillCamera addTarget:filter]; // Begin showing video camera stream [stillCamera startCameraCapture]; }
Baris 5-9 menciptakan GPUImageView
baru untuk menampilkan kamera feed dan instance GPUImageFilter
default untuk menerapkan efek khusus ke tampilan. Kita bisa dengan mudah menggunakan salah satu sub GPUImageFilter
, seperti GPUImageSketchFilter
, tetapi kita sebaliknya akan memulai dengan filter bawaan (yaitu tidak ada manipulasi) dan membiarkan pengguna secara dinamis memilih filter kemudian.
Baris 11-17 instantiate GPU kamera instance dan menerapkan filter yang dibuat sebelumnya untuk kamera sebelum memulai penangkapan.
Langkah 8: Menambahkan kamera kustom di IB
Sebelum kode dari langkah 8 akan bekerja, kita perlu menambahkan class MTCameraViewController
kustom baru saja dibuat untuk proyek Storyboard.
Buka MainStoryboard.storyboard file dan menyeret keluar View Controller baru dari perpustakaan objek. Dengan objek ini dipilih, pergi ke tab Indentitiy inspector dan menetapkan nilai bidang "Class" untuk "MTCameraViewController".
Selanjutnya, drag UIToolbar
ke layar dan mengatur style properti untuk "Black Opaque" di Inspektur atribut. Kemudian tambahkan dua width fleksibel bar tombol item ke toolbar dengan "Take Photo" UIBarButtonItem
di tengah.

Untuk menghubungkan controller view ini ke aliran aplikasi, klik kanan tombol "camera" dari controller tampilan utama dan tarik dipicu segues outlet untuk melihat controller baru:



Bila diminta, pilih "Push" sebagai gaya segue.
Dengan baru ditambahkan segue objek yang masih dipilih, pergi ke "Attributes inspector" dan mengatur indentifier untuk "pushMTCamera". Pergi ke depan dan pastikan bahwa "Push" dipilih dari "Style" drop down.



dengan seque dibuat, memastikan bahwa UIImagePicker
akan tidak lagi ditampilkan bila pengguna menyentuh tombol kamera pada layar app pertama dengan melepas IBAction
outlet dari metode photoFromCamera
.
Akhirnya, pilih tampilan utama MTCameraViewController yang baru dibuat. pergi ke Identity inspector dan set nilai class untuk "GPUImageView".
Sementara bleum sempurna dulu, jika Anda membangun dan menjalankan app sekarang, Anda harus mampu mendorong MTCameraViewController
ke hirarki view dan melihat GPUImageView
menampilkan frame dari kamera secara real-time!
Langkah 9: Tambahkan Realtime Filter seleksi
Kita sekarang dapat menambahkan logika yang diperlukan untuk mengendalikan filter yang diterapkan pada tampilan kamera. Pertama, pergi ke viewDidLoad
: metode dalam MTCameraViewController.m file dan menambahkan kode yang akan menciptakan sebuah "Filter" tombol di bagian atas kanan dari tampilan controller:
- (void)viewDidLoad { [super viewDidLoad]; // Add Filter Button to Interface UIBarButtonItem *filterButton = [[UIBarButtonItem alloc] initWithTitle:@"Filter" style:UIBarButtonItemStylePlain target:self action:@selector(applyImageFilter:)]; self.navigationItem.rightBarButtonItem = filterButton;
Baris 6 di atas, kita membuat UIBarButtonItem kustom yang akan memicu applyImageFilter
: ketika dipilih.
Sekarang membuat metode selector:
- (IBAction)applyImageFilter:(id)sender { UIActionSheet *filterActionSheet = [[UIActionSheet alloc] initWithTitle:@"Select Filter" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Grayscale", @"Sepia", @"Sketch", @"Pixellate", @"Color Invert", @"Toon", @"Pinch Distort", @"None", nil]; [filterActionSheet showFromBarButtonItem:sender animated:YES]; }
Setelah menambahkan di atas Anda akan melihat kompiler peringatan yang menyatakan bahwa saat ini lihat controller tidak mematuhi protokol UIActionSheetDelegate
. Memperbaiki bahwa masalah sekarang dengan pergi ke MTCameraViewController.h dan memodifikasi Deklarasi class kelas seperti:
#import <UIKit/UIKit.h> @interface MTCameraViewController : UIViewController <UIActionSheetDelegate> @end
Lengkap lingkaran dengan kembali ke MTCameraViewController.m file dan menambahkan logika yang akan menanggapi UIActionSheet
yang disajikan:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { // Bail if the cancel button was tapped if(actionSheet.cancelButtonIndex == buttonIndex) { return; } GPUImageFilter *selectedFilter; [stillCamera removeAllTargets]; [filter removeAllTargets]; switch (buttonIndex) { case 0: selectedFilter = [[GPUImageGrayscaleFilter alloc] init]; break; case 1: selectedFilter = [[GPUImageSepiaFilter alloc] init]; break; case 2: selectedFilter = [[GPUImageSketchFilter alloc] init]; break; case 3: selectedFilter = [[GPUImagePixellateFilter alloc] init]; break; case 4: selectedFilter = [[GPUImageColorInvertFilter alloc] init]; break; case 5: selectedFilter = [[GPUImageToonFilter alloc] init]; break; case 6: selectedFilter = [[GPUImagePinchDistortionFilter alloc] init]; break; case 7: selectedFilter = [[GPUImageFilter alloc] init]; break; default: break; } filter = selectedFilter; GPUImageView *filterView = (GPUImageView *)self.view; [filter addTarget:filterView]; [stillCamera addTarget:filter]; }
Baris 11-12 digunakan untuk me-reset filter yang saat ini dipilih.
Baris 15-42 di atas harus akrab bagi logika di ViewController.m; kita hanya menggati tombol yang dipilih untuk membuat sebuah instance filter berhubungan.
Baris 44-47 ambil filter baru yang dibuat dan menerapkannya ke kamera GPUImage.
Jika Anda membangun dan menjalankan proyek sekarang, Anda akan melihat bahwa tombol filter baru dibuat memungkinkan pengguna untuk mencoba GPUImage filter secara real time!
Langkah 10: Membuat protokol delegasi kamera
Sekarang bahwa kita memiliki filter live feed bekerja, langkah besar terakhir dalam tutorial adalah untuk memungkinkan pengguna untuk mengambil foto dengan kamera GPUImage dan kemudian menampilkan mereka kembali dalam tampilan utama controller foto carousel.
Untuk mencapai ini, kita akan memasukan pesan antara tampilan controller menggunakan delegation design pattern. Secara khusus, kami akan membuat protokol kustom resmi mendelegasikan kita sendiri di MTCameraViewController
dan kemudian mengkonfigurasi class ViewController
utama untuk menyesuaikan diri dengan protokol itu untuk menerima delegasi pesan.
Untuk memulai, pergi ke MTViewController.h
dan memodifikasi kode sebagai berikut:
#import <UIKit/UIKit.h> @protocol MTCameraViewControllerDelegate - (void)didSelectStillImage:(NSData *)image withError:(NSError *)error; @end @interface MTCameraViewController : UIViewController @property(weak, nonatomic) id delegate; @end
Kode di atas menyatakan pattern resmi mendelegasikan disebut MTCameraViewControllerDelegate
pada baris 3-7, dan kemudian menciptakan sebuah objek delegasi pada baris 11.
Selanjutnya beralih ke MTCameraViewController.m dan mensintesis properti delegasi:
@implementation MTCameraViewController @synthesize delegate;
Dengan protokol yang dideklarasi, kita sekarang perlu untuk menerapkannya dalam class ViewController
utama. Pergi ke ViewController.h
dan tambahkan baris berikut:
#import <UIKit/UIKit.h> #import "iCarousel.h" #import "MTCameraViewController.h" @interface ViewController : UIViewController <UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIActionSheetDelegate, MTCameraViewControllerDelegate, iCarouselDataSource, iCarouselDelegate> @end
Sekarang membuka ViewController.m file. Kami ingin menetapkan properti delegasi bila controller di inisialisasi. Karena kita menggunakan storyboard, tempat yang tepat untuk melakukan ini adalah di prepareForSegue:sender:
metode, yang akan dipanggil sebelum view baru controller didorong ke layar:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([segue.identifier isEqualToString:@"pushMTCamera"]) { // Set the delegate so this controller can received snapped photos MTCameraViewController *cameraViewController = (MTCameraViewController *) segue.destinationViewController; cameraViewController.delegate = self; } }
Selanjutnya kita perlu untuk mengimplementasikan didSelectStillImage:withError
: metode yang diperlukan oleh protokol MTCameraViewControllerDelegate
:
#pragma mark - #pragma mark MTCameraViewController // This delegate method is called after our custom camera class takes a photo - (void)didSelectStillImage:(NSData *)imageData withError:(NSError *)error { if(!error) { UIImage *image = [[UIImage alloc] initWithData:imageData]; [displayImages addObject:image]; [self.photoCarousel reloadData]; self.filterButton.enabled = YES; self.saveButton.enabled = YES; } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Capture Error" message:@"Unable to capture photo." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } }
Kode di atas akan mengkonversi NSData
objek menyerahkan kepada metode untuk UIImage
dan kemudian reload foto carousel.
Akhirnya, kita perlu untuk membungkus hal dengan mengembalikan MTCameraViewController.m dan menambahkan dalam panggilan metode delegasi sesuai. Pertama, men-setup sebuah metode IBAction
yang akan memicu snap kamera:
GPUImageFilter *filter; } - (IBAction)captureImage:(id)sender; @end
Sebelum melanjutkan, menghubungkan metode ini untuk "Take Photo" tombol di MainStoryboard.storyboard file.
Akhirnya, tambahkan implementasi metode:
-(IBAction)captureImage:(id)sender { // Disable to prevent multiple taps while processing UIButton *captureButton = (UIButton *)sender; captureButton.enabled = NO; // Snap Image from GPU camera, send back to main view controller [stillCamera capturePhotoAsJPEGProcessedUpToFilter:filter withCompletionHandler:^(NSData *processedJPEG, NSError *error) { if([delegate respondsToSelector:@selector(didSelectStillImage:withError:)]) { [self.delegate didSelectStillImage:processedJPEG withError:error]; } else { NSLog(@"Delegate did not respond to message"); } runOnMainQueueWithoutDeadlocking(^{ [self.navigationController popToRootViewControllerAnimated:YES]; }); }]; }
Baris atas 3-5 menonaktifkan tombol "Take Phoyo" untuk mencegah beberapa tekan saat pemrosesan.
Baris 7-22 menggunakan GPUImage metode capturePhotoAsJPEGProcessedUpToFilter:withCompletionHandler
: untuk benar-benar menyimpan gambar JPEG, periksa untuk melihat jika seorang delegasi telah ditetapkan, dan kemudian mengirim data gambar ke delegasi jika sudah diatur.
Baris 19 muncul saat ini view controller, tapi seterusnya thread aplikasi utama.
Ringkasan
Selamat! Jika Anda telah mengikuti tutorial sejauh ini, maka Anda harus sepenuhnya fungsional, advanced aplikasi pengambil foto! Jika Anda memiliki pertanyaan atau umpan balik, jangan ragu untuk meninggalkannya di bagian komentar di bawah ini atau mengirimkannya kepada saya secara langsung di Twitter (@markhammonds).
Terima kasih sudah membaca!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weekly