Kuidas luua Stop Motioni animatsioonikaamera, kasutades iOS-is AVFoundationit

Kaamera ehitamine on sotsiaalmeedia iOS-i rakenduse väljatöötamisel üks tavalisemaid ülesandeid. Selles artiklis ehitame stopp-animatsiooni efekti loomiseks kaameraga rakenduse. Liikumise peatamise efekt saavutatakse pildiseeria mängimisega kiire järjestusena, et luua liikumisest illusioon.

AVFoundationi abil loodud Stop-Motion - õpetuse lõpp, teil peaks olema rakendus, mis seda teeks :)
Esiteks saab kaamera funktsionaalsusega iOS-i rakenduse loomist teha kahel erineval viisil. See kasutab kas UIImagePickerControllerit või AVFoundationi raamistikku.

Mis on AVFoundation?

AVFoundation on iOS-i raamistik, mida kasutatakse teie rakenduse audiovisuaalsete funktsioonide loomiseks. Selle raamistiku abil saate manipuleerida heli- ja videovara hõivamise, töötlemise, redigeerimise, importimise ja eksportimisega.

UIImagePickerController vs AVFoundation:

UIImagePickerControlleri abil saame täita kõiki põhifunktsioone, nagu näiteks pildistamine, välklambi lülitamine, kaamera vahetamine, fookuse ja särituste redigeerimine. Samuti võimaldab see teil juurdepääsu fotokogule piltide salvestamiseks ja jagamiseks. Seda on AVFoundationiga võrreldes lihtsam rakendada.

Kuid AVFoundation tagab kaamera sätete ja meediumiga manipuleerimise täieliku kontrolli. See võimaldab teil UIImagePickerControllerina teha kõiki põhitoiminguid ning lisaks sellele saate töötleda jäädvustamise, taasesituse tooreid andmeid, luua videost pisipilte / pilte, redigeerida ja muuta selliste sätete sätteid nagu fookus, säritus jms fotode ja videomeediumide jäädvustamiseks.

Kuidas AVFoundationi kasutada?

AVFoundationil on 5 põhiklassi:

  • AVCaptureDevice: tähistab füüsilist jäädvustusseadet, näiteks kaamerat ja mikrofoni. Igas iPhone'is on üks mikrofon (helisisend) ja kaks kaamerat (visuaalse sisendi jaoks ees ja taga). Selle abil saate sisestusseadme sätteid enne salvestamissessioonile edastamist konfigureerida.
  • AVCaptureDeviceInput: pakub meediumit jäädvustusseadmest hõivamisseanssini.
  • AVCaptureOutput: tähistab püüdmisseadme väljundit. Kaamera väljund võib olla üks järgmistest - AVCaptureMovieFileOutput, AVCaptureVideoDataOutput, AVCaptureAudioDataOutput või AVCapturePhotoOutput. AVCapturePhotoOutput esindab stoppkaadri väljundit. Saame konfigureerida AVCapturePhotoOutput väljundi sätteid nagu eelseade, andmevorming ja töötlemata andmete esitusseaded.
  • AVCaptureSession: toimib AV-sisendite ja -väljundite vahelise koordinaatorina. Sa initsialiseerid hõiveseansi, lisad sessioonile jäädvustamise sisendi ja väljundi. Seejärel alustate seansi käitamist, et luua andmevoog sisendi ja väljundi vahel.
  • AVCaptureVideoPreviewLayer: lisaks eeltoodule kasutatakse seda ka pildistatava või salvestatava reaalajas eelvaate saamiseks kasutajale.

Nüüd, alustades neid põhitõdesid, saate alustada rakenduse loomist:

Stopp-liikumisefekti saavutamiseks laseme kasutajal teha fotoseeria ja luua GIF-faili, kasutades AVFoundation-i raamistikku. Rakendus nõuab kõiki UIViewControlleri klasse ja klassi, et abistada kõiki kaamera funktsioone, mida me kasutame:

  • ViewController - vaike algne vaatekontroller, mis genereeritakse uue ühevaatelise rakenduse loomisel. Kasutame seda reaalajas kaamera näitamiseks nuppudega kaamera sätete muutmiseks, foto tegemiseks ja stopp-meediumi loomiseks.
  • PreviewViewController - näitab stopperi eelvaadet võimalusega salvestada see fototeekisse.
  • CameraSetup - abstraktsioon kõigi meie kasutatavate kaamerafunktsioonide kohta. Selles klassis kasutame AVFoundationi raamistikku. ClassViewController klass sisaldab CameraSetup eksemplari.

TL: DR Töötava koodi leiate minu git-hoidlast.

  1. Alustame jutustahvli seadistamisega. Looge ühe kuvaga rakenduse jaoks projekt. Algses ViewControlleris lisage lihtsalt teine ​​vaade ja määrake selle servad supervaate servadele. Lisage kokku 4 nuppu, nagu on näidatud allpool - kaamera vahetamiseks, välgu lülitamiseks, pildistamiseks ja nupule Valmis, et salvestatud piltide komplektist luua GIF-i stop motion.

2. Looge uus kakaopuudutuse klassi fail, mis on UIViewControlleri alamklass. Pange see nimeks “PreviewViewController”, kuna see näitab loodud stop motion GIF-i viimast eelvaadet.

3. Lisage süžeeskeemile veel üks vaatekontroller. Lisage pildi vaade ja määrake selle servad supervaate servadeks. Lisage siia kaks nuppu - üks GIF-i fototeekisse salvestamiseks ja teine ​​- loobumine ilma salvestamata. Määrake oma identiteedi inspektoris klassiks PreviewViewController.

4. Nüüd looge vaade ViewControllerist PreviewViewControllerisse ja määrake selle atribuutide inspektoris parameetriks “showPreview”. Kasutame seda koodis üleminekuks esimeselt teisele kontrollerile.

5. Looge uus kaameraklassi CameraSetup jaoks kiire klass, millesse me rakendame AVFoundation. Ja me lähtestaksime klassi objekti ViewControlleris kaameratoimingute tegemiseks.

Keskendume CameraSetup klassile, kus hakkame kasutama AVFoundationit.

import AVFoundation
Klassi kaamera seadistamine {
....
}

Deklareerime klassifailis kõigepealt vajalike muutujate loendi.

// kaamerasalvestuse seanss initsialiseeritakse
var captureSession = AVCaptureSession ()
// Pildi tegemiseks vajaliku salvestusseadme loetelu
var frontCam: AVCaptureDevice?
var rearCam: AVCaptureDevice?
var currentCam: AVCaptureDevice?
// sisend ja väljund jäädvustamiseks
var captureInput: AVCaptureDeviceInput?
var captureOutput: AVCapturePhotoOutput?
// kaamera eelvaate kiht
var previewLayer: AVCaptureVideoPreviewLayer?

Nüüd laseb luua mingi funktsiooni püüdmisseansi konfigureerimiseks ja käivitamiseks. Alustame rakendusega CaptureDevice

func captureDevice () {
lase discoverySession = AVCaptureDevice.DiscoverySession (deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .specified)
        d jaoks discoverySession.devices {
            kui d.positsioon ==. ees {
                frontCam = d
            }
            muul juhul, kui d.positsioon ==. tagasi {
                rearCam = d
                tee {
                    proovige rearCam? .lockForConfiguration ()
                    rearCam? .focusMode = .autoFocus
                    rearCam? .exposureMode = .autoExpose
                    rearCam? .unlockForConfiguration ()
                }
                saagi laskmise viga {
                    print (viga)
                }
            }
        }
    }

DiscoverySession päringu abil pakub meile füüsiliste sisendseadmete loendit, mis vastab täpsustatud kriteeriumidele. Siinkohal soovime loendit seadmetest, millel on sisseehitatud lainurkokaamera ja mis võivad kasutada visuaalset meediat. Siis seadsime seadme eesmisse kaamerasse, kui seadme asukoht on ees ja sama kehtib ka tagaosa kohta.

Redacami abil olen konfigureerinud selle fookusrežiimi ja särituse. Need on mõned funktsioonid, mida saab kaamera AVFoundation abil konfigureerida, kui seadistate kaamera jäädvustamiseks.

Meil on eesmine ja tagune kaamera, mida saaksime lisada pildistamise sisenditena. Võimaldab luua funktsiooni captureInput konfigureerimiseks ja seansi sisendi lisamiseks.

func configureCaptureInput () {
        currentCam = rearCam!
        tee {
            captureInput = proovige AVCaptureDeviceInput (seade: currentCam!)
            kui captureSession.canAddInput (captureInput!) {
                captureSession.addInput (captureInput!)
            }
        }
        saagi laskmise viga {
            print (viga)
        }
    }

Siin kontrollime, kas sisendit saab sessioonile lisada, kui see on tõene, lisame selle. Samamoodi peame konfigureerima ja lisama seansi CaptureOutput. Nii saame lisada klassi järgmise funktsiooni,

func configureCaptureOutput () {
captureOutput = AVCapturePhotoOutput ()
        captureOutput! .setPreparedPhotoSettingsArray ([AVCapturePhotoSettings (vorming: [AVVideoCodecKey: AVVideoCodecType.jpeg])]], lõpuleviimineHandler: null)
        kui captureSession.canAddOutput (captureOutput!) {
            captureSession.addOutput (captureOutput!)
        }
        captureSession.startRunning ()
}

setPreparedPhotoSettingsArray seab meie soovi fotoväljundi sätte. Kui captureOutput on sessioonile lisatud, on aeg seansi alustamiseks. Sel eesmärgil kutsume startRunning ().

Lõpuks jääb viiest klassist alles AVVideoPreviewLayer. Läheme edasi ja lisage see viimane osa.

func configurePreviewLayer (vaade: UIView) {
  previewLayer = AVCaptureVideoPreviewLayer (seanss: captureSession)
  
  previewLayer? .videoGravity = AVLayerVideoGravity.resizeAspectFill
  previewLayer? .connection? .videoOrientation = .portrait
        
  view.layer.insertSublayer (previewLayer !, kell: 0)
  previewLayer? .frame = view.frame
 }

Selle funktsiooni jaoks annaksime üle UIView, mille lõime esimeses ViewControlleris. AVCaptureVideoPreviewLayer on CALayeri (põhianimatsioon) alamklass ja töötab samaaegselt antud captureSessioniga.

6. Minge ViewControlleri juurde, ühendage vaade, nupp ja selle toimingud storyboardil vaatekontrolleriga. See näeks välja umbes selline,

Kõigi konfiguratsioonifunktsioonide kutsumiseks CameraSetupist lisage funktsioon, mida nimetatakse initsialiseerimiseks.

var cameraSetup: CameraSetup!
...
func lähtestama () {
        cameraSetup = CameraSetup ()
        cameraSetup.captureDevice ()
        cameraSetup.configureCaptureInput ()
        cameraSetup.configureCaptureOutput ()
        cameraSetup.configurePreviewLayer (vaade: camView)
    }

Märkus. Te ei saa seda simulaatoris käivitada. Käivitage rakendus oma iPhone'is. Te peaksite eelvaadet nägema.

Pange tähele, et klõpsamise nupul klõpsates ei juhtu midagi. Selle põhjuseks on asjaolu, et me ei ole seda funktsiooni veel seadistanud. Lisagem siis, lisage see nüüd vaatekontrollis.

Kujutise jäädvustamine on asünkroonne toiming. See tähendab, et peate edastama tagasihelistamise funktsiooni AVFoundationile, mis helistatakse kohe pärast pildi jäädvustamist.
var previewImage = [UIImage] ()
...
@IBAction func captureAction (_ saatja: ükskõik) {
        cameraSetup.captureImage {(pilt, viga)
            valvur lase pilt = pilt veel {
                print (tõrge? "Pildi hõive viga")
                tagasi
            }
            self.previewImage.append (pilt)
        }
}

Loome UIImage-massiivi, et salvestada kõik pildid, mis on loodud stopp-liikumise loomiseks. In captureAction nimetame CameraSetup funktsiooni captureImage koos parameetriga sulgur. Sel juhul suletud pilt salvestatakse massiivi.

Klassis CameraSetup lisage järgmine funktsioon

var photoCaptureCompletionBlock: ((UIImage ?, viga?) -> Void)?
...
func captureImage (lõpuleviimine: @escaping (UIImage?, viga?) -> tühine) {
        lase seaded = AVCapturePhotoSettings ()
        settings.flashMode = self.flashMode
        
        self.captureOutput? .capturePhoto (koos: sätetega, volitaja: ise kui AVCapturePhotoCaptureDelegate)
        self.photoCaptureCompletionBlock = lõpetamine
    }

photoCaptureCompletionBlock on meie captureActioni raames määratletud sulgemise nimi.

capturePhoto kasutatakse pildistamiseks foto täpsustatud sätetega, näiteks välklambiga, andmevorminguga jne. Selle kõne rakendamiseks peab olema AVCapturePhotoCaptureDelegate. Pange tähele, et oleme teinud end delegaadiks ja laseme seega lisada funktsiooni, et jälgida delegaadi protokolli.

public func photoOutput (_ väljund: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        kui las x = viga {
            self.photoCaptureCompletionBlock? (null, x)
        }
        muidu kui andke andmed = photo.fileDataRepresentation (), laske pilt = UIImage (andmed: andmed) {
            self.photoCaptureCompletionBlock? (pilt, null)
        }
    }

DidFinishProcessingPhoto pakub meile töödeldud lõpliku pildi, mille me talletame oma massiivi.

7. ViewControlleri nupu Valmis funktsioonid hõlmavad ka setti ettevalmistamist ja läbiviimist. Pidage meeles meie loodud segue “showPreview”, mida siin nimetatakse, kui klõpsatakse nuppu Valmis. Nupu Valmis määratlus,

@IBAction func doneAction (_ saatja: ükskõik) {
    self.performSegue (withIdentifier: "showPreview", saatja: self)
    }
funktsioonide ettevalmistamine alistada (segue jaoks: UIStoryboardSegue, saatja: kas?) {
   kui lasta sihtkoht = segue.destination as? PreviewViewController {
            Destination.pImg = self.previewImage
        }
    }

8. Avage oma PreviewViewController, ühendage UIImageView vaatekontrolleriga. Looge UIImage massiiv nimega “pImg” ja taimer. Lisage vaatesseDidLoad () järgmine tekst.

alistada funktsiooni viewDidLoad () {
  super.viewDidLoad ()
  preview.image = pImg [0]
  taimer = Timer.scheduledTimer (timeInterval: 0,5, target: self, selector: #selector (self.display), userInfo: null, kordub: true)
    }
    
    @objc func display () {
        kui! (pImg.count == loendur) {
            preview.image = pImg [counter]
            loendur + = 1
        }
        veel {
            loendur = 0
        }
    }

Valitud piltide eelvaate kuvamiseks () abil on loodud lihtne taimer.

Oleme peaaegu valmis. Võimaldab lisada salvestamise ja sulgemise nupu toiminguplokid. Lisage closeAction (), kus me muudame taimeri kehtetuks ja loobume PreviewViewControllerist.

@IBAction func closeAction (_ saatja: ükskõik) {
        timer.invalidete ()
        vallandada (animeeritud: tõsi, lõpetamine: null)
    }

Kui soovite fotokogus GIF-vormingus pildikomplekti salvestada, tehke järgmist

@IBAction func saveAction (_ saatja: ükskõik) {
        createGIF (pildid: pImg)
        vallandada (animeeritud: tõsi, lõpetamine: null)
    }

Funktsioon createGIF loob kCGImagePropertyGIFDictionary abil GIF-i ja seejärel loob pildiandmeid sisaldava URL-i. Sellelt URL-ilt saate PHPhotoLibrary abil luua ja lisada fototeekisse vara. (viitamiseks)

fotode importimine
...
func createGIF (pildid: [UIImage]) {
        lase fileProperties: CFD Dictionary = [kCGImagePropertyGIFDictionary kui string: [kCGImagePropertyGIFLoopCount as String: 0]] kui CFD Dictionary
        lase frameProperties: CFD Dictionary = [kCGImagePropertyGIFD Dictionary kui string: [kCGImagePropertyGIFUnclampedDelayTime as String: 0.5]] kui CFD Dictionary
        
        las dokumendidDirectoryURL: URL? = proovida? FileManager.default.url (for: .documentDirectory, in: .userDomainMask, tinkaFor: null, looge: true)
        kas lasta failURL: URL? =ocsDirectoryURL? .apppingPathComponent ("animated.gif")
        kui lase url = fileURL kui CFURL? {
            kui lase sihtkoht = CGImageDestinationCreateWithURL (URL, kUTTypeGIF, images.count, null) {
  CGImageDestinationSetProperties (sihtkoht, fileProperties)
piltide jaoks pilt {
 if let cgImage = image.cgImage {
  CGImageDestinationAddImage (sihtkoht, cgImage, frameProperties)
 }
}
kui! CGImageDestinationFinalise (sihtkoht) {
   print ("Kujutise sihtpunkti vormistamine ebaõnnestus")
}
                print ("Url = \ (fileURL!)")
}
}
 
// Taotlege kujutiselt vara loomist ja selle salvestamist
// Fotoraamatukogu.
PHPhotoLibrary.shared (). ExecChanges ({PHAssetChangeRequest.creationRequestForAssetFromImage (atFileURL: fileURL!)
        })
    }

Ja oleme valmis.

Nüüd on jäänud vaid kaamera ja välgu funktsioonid, mis on valikulised. Kaamera ümberlülitamiseks, kui seansil on rearCam, eemaldame seansilt kõik sisendid ja lisame frontCam uue captureInput ning muudame currentCam frontCam ja vastupidi.

func toggleCam () {
        captureSession.beginConfiguration ()
        lase newCam = (currentCam? .positsioon == .front)? rearCam: eesmine kaamera
        
        sisendiks captureSession.inputsi {
            captureSession.removeInput (sisestusvorm! AVCaptureDeviceInput)
        }
        
        currentCam = newCam
        tee {
            captureInput = proovige AVCaptureDeviceInput (seade: currentCam!)
            kui captureSession.canAddInput (captureInput!) {
                captureSession.addInput (captureInput!)
            }
        }
        saagi laskmise viga {
            print (viga)
        }
        
        captureSession.commitConfiguration ()
    }

Selle funktsiooni helistamiseks rakenduses cameraSoggle vaateaknas klõpsake funktsiooni CameraSetup,

@IBAction func cameraToggle (_ saatja: suvaline) {
        cameraSetup.toggleCam ()
    }

Sarnaselt välklambi lülitamiseks muutke lihtsalt muutuja flashMode väärtust.

@IBAction func flashToggle (_ saatja: ükskõik) {
 kui cameraSetup.flashMode == .off {
     flashButton.setImage (UIImage (nimega "flash_on"), jaoks: .normal)
     cameraSetup.flashMode = .on
 }
 veel {
     flashButton.setImage (UIImage (nimega "flash_off"), jaoks: .normal)
      cameraSetup.flashMode = .off
 }
}

See on nüüd, kui meil on täielik funktsionaalne rakendus. Käivitage rakendus. Väljund peaks olema selline,

Kogu projekt on saadaval allpool nimetatud GitHubi lingi kaudu. Tervitan kogu tagasisidet ja küsimusi. Looge oma kohandatud kaamera nüüd AVFoundationi abil. Head kodeerimist .. :)