How to implement custom camera preview in iOS? - swift4

I am trying to implement Custom camera effect like:- Image
I Thought that this is achieve like this way
This type of functionality already implemented in one app which is available in App Store. here is the link enter link description here
I want to copy this app's camera functionality.
I have already implemented something like this.
I am using below code for achieved above functionality.
Into ViewController.swift class.
import UIKit
import AVFoundation
#available(iOS 10.0, *)
class ViewController: UIViewController
{
#IBOutlet weak var vc: UIView!
#IBOutlet weak var img: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
setupCamera()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
session.startRunning()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
session.stopRunning()
}
#IBOutlet fileprivate var previewView: PreviewView! {
didSet {
previewView.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewView.layer.cornerRadius = previewView.layer.frame.size.width/2
previewView.clipsToBounds = true
}
}
#IBOutlet fileprivate var imageView: UIImageView! {
didSet {
imageView.layer.cornerRadius = imageView.layer.frame.size.width/2
imageView.clipsToBounds = true
}
}
fileprivate let session: AVCaptureSession = {
let session = AVCaptureSession()
session.sessionPreset = AVCaptureSessionPresetPhoto
return session
}()
fileprivate let output = AVCaptureStillImageOutput()
}
#available(iOS 10.0, *)
extension ViewController {
func setupCamera() {
let backCamera = AVCaptureDevice.defaultDevice(withMediaType:
AVMediaTypeVideo)
guard let input = try? AVCaptureDeviceInput(device: backCamera) else {
fatalError("back camera not functional.") }
session.addInput(input)
session.addOutput(output)
previewView.session = session
}
}
// MARK: - #IBActions
#available(iOS 10.0, *)
private extension ViewController {
#IBAction func capturePhoto() {
if let videoConnection = output.connection(withMediaType: AVMediaTypeVideo) {
output.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
if let cameraImage = UIImage(data: imageData) {
self.imageView.image = cameraImage
UIImageWriteToSavedPhotosAlbum(cameraImage, nil, nil, nil)
}
}
})
}
}
}
Also Create Preview Class and this class into UIView from storyboard file.
From above code I have achived this image.
I need to add any shape of image layer as a frame into UIView. ButI have no idea how to achieved this type of functionality.
So, Basically my task is, how to add any shape of image layer into UIView and after capture image how to save image with image layer, like Final Image clue image

Related

How to execute delegate method once calling capturePhoto(with: , delegate: ) method

This is my code.
When I click the captureButton it executes without error.
The purpose is to save a CIImage in captureProcessor.image
import UIKit
import AVFoundation
class ViewController: UIViewController {
let captureProcessor = captureProcess()
let session = AVCaptureSession()
let capturedPhotoOutPut = AVCapturePhotoOutput()
#IBOutlet weak var previewOfCamera: UIView!
#IBOutlet weak var imageShow: UIImageView!
#IBAction func captureButton(_ sender: Any) {
captureProcessor.capturePhoto(capturedPhotoOutPut)
// getCGRectInfo()
// code above is to get CGRect info from captureProcessor.image
}
override func viewDidLoad() {
super.viewDidLoad()
configurePreview()
}
func configureTheSession () -> Void {
var videoInput:AVCaptureDeviceInput!
let videoDevice = AVCaptureDevice.DiscoverySession(deviceType:[.builtInWideAngleCamera], mediaType: .video, position: .back).devices.first
videoInput = try! AVCaptureDeviceInput(device: videoDevice!)
session.beginConfiguration()
session.sessionPreset = .hd1280x720
session.addInput(videoInput)
session.addOutput(capturedPhotoOutPut)
session.commitConfiguration()
DispatchQueue.global(qos: .userInitiated).async {
self.session.startRunning()
}
}
And here is another class mentioned above
import Foundation
import AVFoundation
class captureProcess: NSObject, AVCapturePhotoCaptureDelegate{
var image:CIImage!
func capturePhoto (_ photoOutput: AVCapturePhotoOutput) {
let settings = AVCapturePhotoSettings()
settings.flashMode = .off
photoOutput.capturePhoto(with: settings, delegate: self)
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
let imageData = photo.fileDataRepresentation()
image = CIImage(data: imageData!)
}
But if I add an action in #IBAction, e.g. getInfo (comment in first chunk of code). Program will omit capturePhoto (it runs this line but doesn't execute the delegate) but executes getCGRectInfo first. The image variable is still empty. So the program will crash.
Is there any way to execute delegate method once the program run to capturePhoto line so that I can get the CIImage info properly

Swift 5 - Issues With Passing Data From Class To Class

As an exercise to learn Swift, I'm creating a simple app where you use ImagePickerController to select a photo and get data about the photo. For now, I'm just pulling pixelWidth and pixelHeight data from photo's PHAsset.
My Setup: I have a ViewController class which I created using Storyboard that includes the UI, ImagePickerController and it's delegate which after selecting photo, will update data in another class called TestGlobalData.
The problem I'm running into is that while I'm able to update variables from ViewController to TestGlobalData, I can't get it to update back on ViewController
Here is my code. Any help would be appreciated, I'm totally stumped (As mentioned I'm also new to Swift, so pointing out any fundamental things I'm not getting would be appreciated too! )
// TestViewController.swift
class TestViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var testPhotoView: UIImageView!
#IBOutlet weak var testWidthLabel: UILabel!
#IBOutlet weak var testHeightLabel: UILabel!
var testWidthText: String?
var testHeightText: String?
var selectionFromPicker: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
}
// Get imagePickerController ///////////////////////////////////////////////////////////////////
#IBAction func getPhotoButton(_ sender: Any) {
getImagePicker()
}
func getImagePicker() {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
imagePickerController.sourceType = .photoLibrary
imagePickerController.allowsEditing = false
present (imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
defer { dismiss (animated: true, completion: nil)}
guard let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
guard let asset = info[UIImagePickerController.InfoKey.phAsset] as? PHAsset else { return }
selectionFromPicker = selectedImage
let data = TestGlobalData()
data.testData = asset // Updates PHAsset
data.updateData() // Data shows as updated here
data.pushData() // Data shows as updated here too
self.updateTestPhoto() // Photo updates successfully (photo does not get passed)
self.textToLabel() // Assigns text to UILobel
self.checkData() // Data is lost and shows as nil here
}
// Functions //////////////////////////////////////////////////////////////////////////////
// Assign Text To Label
func textToLabel() {
testWidthLabel.text = testWidthText
testHeightLabel.text = testHeightText
}
// Update Photo To Selected
func updateTestPhoto() {
testPhotoView.image = selectionFromPicker
}
// Final Check
// TestGlobalData.swift
class TestGlobalData {
var testData: PHAsset?
var testWidth = Int()
var testHeight = Int()
var widthInString = String()
var heightInString = String()
func updateData() {
testWidth = testData!.pixelWidth
testHeight = testData!.pixelHeight
widthInString = String(testWidth)
heightInString = String(testHeight)
//widthInString and testWidth updated successfully at this point
}
func pushData() {
let vc = TestViewController()
vc.testWidthText = widthInString
vc.testHeightText = heightInString
//vc.testWidthText show as updated successfully here
}
}
The problem is you are creating a new instance of the TestViewController in the TestGlobalData class, specifically in the pushData() function.
Instead change the pushData to:
func pushData(vc: UIViewController) {
vc.testWidthText = widthInString
vc.testHeightText = heightInString
}
and change when you call the method as well to:
data.pushData(self)
Here is some additional resources that should help you understand everything better :)
https://code.tutsplus.com/tutorials/swift-from-scratch-an-introduction-to-classes-and-structures--cms-23197
https://www.python-course.eu/python3_class_and_instance_attributes.php

How to modify previewView in swift?

I'm building camera App.
I want to preview and photo frame 1:1.
But how can I do that?
I've tried previewView frame change.
self.previewView?.frame.size = CGSize(width: 300, height: 300)
But It does not working.
class CameraViewController: UIViewController {
// MARK: - Properties
// MARK: Declared
var captureSession: AVCaptureSession?
var captureOutput: AVCapturePhotoOutput?
// MARK: IBOutlet
#IBOutlet weak var previewView: PreviewView!
// MARK: - Methods
// MARK: View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
self.configureInput()
self.configureOutput()
self.configurePreview()
self.runCamera()
}
// MARK: Configure
private func configureInput() {
self.captureSession = AVCaptureSession()
self.captureSession?.beginConfiguration()
self.captureSession?.sessionPreset = .hd4K3840x2160
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice), self.captureSession?.canAddInput(videoDeviceInput) == true else { return }
self.captureSession?.addInput(videoDeviceInput)
}
private func configureOutput() {
let photoOutput = AVCapturePhotoOutput()
self.captureOutput = photoOutput
guard self.captureSession?.canAddOutput(photoOutput) == true else { return }
self.captureSession?.sessionPreset = .photo
self.captureSession?.addOutput(photoOutput)
self.captureSession?.commitConfiguration()
}
private func configurePreview() {
self.previewView?.videoPreviewlayer.session = self.captureSession
}
private func runCamera() {
self.captureSession?.startRunning()
}
}
This is my code.
I made this read after apple's article. (https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/setting_up_a_capture_session)
You could use this to change the preview layer frame to make it fill your preview view:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
previewLayer.frame = cameraView.bounds
previewLayer.videoGravity = .resizeAspectFill
}

Why won't my AVCaptureVideoPreviewLayer appear?

I'm trying to make a Swift OsX app that opens the camera and displays what's being recorded.
Right now it's literally just one ViewController, the green light turns on which makes me think the recording is starting, but the screen stays grey and nothing appears.
Here's the code for the ViewController:
class ViewController: NSViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
let session = AVCaptureSession()
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
#IBOutlet weak var imgView: NSImageView!
func setUpSession() {
self.session.sessionPreset = AVCaptureSessionPresetLow
// Add the default input camera
do {
let input = try AVCaptureDeviceInput(device: device)
self.session.addInput(input)
print("Input added.")
} catch let error as NSError {
print(error)
}
var previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.frame = self.view.bounds
previewLayer.position = CGPointMake(0, 0)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.view.layer = previewLayer
self.session.startRunning()
print("Session running.")
}
override func viewDidLoad() {
super.viewDidLoad()
self.setUpSession()
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Try view.layer.addSublayer(previewLayer)

Changing the view color when comparing values

I created a view to use as background and I would like to change its color when label text is greater or less than variable number. The script is okay but the color is not changing.
Thanks in advance.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var localName: UITextField!
#IBOutlet weak var localNameLabel: UILabel!
#IBOutlet weak var localTemp: UILabel!
#IBAction func getData(sender: AnyObject) {
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=" + localName.text! + "")
}
#IBOutlet weak var fundo: UIView!
override func viewDidLoad() {
super.viewDidLoad()
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=London")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getWeatherData(urlString: String){
let url = NSURL (string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data!)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options:NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(json)
//localNameLabel.text = json[("name")] as? String
if let name = json[("name")] as? String {
localNameLabel.text = name
}
if let main = json[("main")] as? NSDictionary {
if let temp = main[("temp")] as? Double {
//convert kelvin to celsius
let ft = (temp - 273.15)
let myString = ft.description
localTemp.text = myString
self.changeColor()
}
}
} catch let error as NSError {
print(error)
}
var number : Float
func changeColor(){
number = 19.0
if(Float(localTemp.text!) < number){
fundo.backgroundColor = .blueColor()
}else{
fundo.backgroundColor = .orangeColor()
}
}
}
}
Edited to post the entire script
In your view controller you need to add UITextFieldDelegate which will allow you to access methods related to your text field. The top of your view controller should look like this:
class ViewController: UIViewController,UITextFieldDelegate //set delegate to class
You then need to set the delegate of your text field to self in viewDidLoad and add a target for when the text field changes:
override func viewDidLoad() {
super.viewDidLoad()
localTemp.delegate = self //set delegate to this vc
localTemp.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
You can then implement this method which will run on every key press and you need to call your changeColor() method as above:
func textFieldDidChange(textField: UITextField) {
self.changeColor()
}