How to send image from framework to sample project in iOS Swift? - ios

I have implemented document scanner using visionKit. Initially i have faced camera dismiss issue and it fixed. Now i am trying to send image after camera dismiss from framework to sample project.
I have tried using completion handler, but it does not work.
Here is the code for framework:
public class A8Scan: NSObject, VNDocumentCameraViewControllerDelegate {
var imageNew: UIImage?
var statusImage: UIImageView?
private var clientView: UIViewController?
public init(_ viewController:UIViewController){
self.clientView = viewController
}
public func showScanner(imgData: UIImage?){
self.createTaskController(img: imgData)
print("Called Build")
}
private func createTaskController(img: UIImage?){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: {
self.imageNew = img
})
}
public func imageFromFile(result: #escaping (_ image: UIImage?) -> Void){
//the image
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
if imageNew != nil {
result(imageNew)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(nil)
}
}
func set(image: UIImage) {
self.statusImage?.image = image
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
print("new image::\(newImage.size)")
controller.dismiss(animated: true, completion: nil)
// processImage(newImage)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
public func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
}
Here is the code sample project:
#IBAction func btnAction(_ sender: Any) {
// A8Scan(self).showScanner()
// A8Scan(self).showScanner(imgData: im)
// print("nn", A8Scan(self).showScanner(imgData: im))
A8Scan(self).imageFromFile { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
// p()
}
func p (){
ScannerViewController().imageFromFile{(image: UIImage?) -> Void in
//use the image that was just retrieved
print("image data", image)
}
}
My issue is after camera dismiss from framework it does not send image from framework to sample project.
Any help much appreciated pls....

1-
Add this inside your framework
var callBack((UIImage)-())?
public func show() {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
}
2-
let newImage = compressedImage(originalImage)
callBack?(newImage)
3-
Then inside the vc that uses it
let vc = A8Scan(self)
vc.show()
vc.callBack = { [weak self] image in
}

As you are using a middle man (A8Scan) NSObject to present a controller. the A8Scan will get deallocated immediately after presenting the controller which you can confirm by keeping a deinit function inside A8Scan.
A workaround for this is to make your NSObject (A8Scan) as a singleton ie add
static let shared = A8Scan()
var callBack: ((_ image: UIImage?) -> Void)?
remove the existing init method and add the viewcontroller property into the imageFromFile: method, your function will look like this
public func imageFromFile(sender: UIViewController, result: #escaping (_ image: UIImage?) -> Void){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.callBack = result
sender.present(scannerViewController,animated:true,completion: nil)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
callBack?(imageNew!)
controller.dismiss(animated: true, completion: nil)
}
and finally in your sample project,
#IBAction func btnAction(_ sender: Any) {
A8Scan.shared.imageFromFile(sender: self) { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
}

Related

How to dismiss camera controller from framework in iOS Swift?

I have implemented vision document scanner inside framework. When camera view controller is called and document captured. While save button tapped it should dismiss and return to viewController.
Here is the code inside framework:
public func showScanner(){
self.createTaskController()
// let scannerViewController = VNDocumentCameraViewController()
// scannerViewController.delegate = self
// present(scannerViewController, animated: true)
print("Called Build")
}
private func createTaskController(){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.addChild(scannerViewController)
self.clientView?.view.addSubview(scannerViewController.view)
scannerViewController.didMove(toParent: clientView)
scannerViewController.dismiss(animated: true)
}
public func imageFromFile(result: #escaping (_ image: UIImage?) -> Void){
//the image
if imageNew != nil {
result(imageNew)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(nil)
}
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
print("new image::\(newImage.size)")
print("new imagei::\(newImage)")
controller.dismiss(animated: true)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
public func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
Here is the code where i have called framework inside sample project:
#IBAction func btnAction(_ sender: Any) {
A8Scan(self).showScanner()
p()
}
My issue is when tapping on save button it should dismiss camera controller (VNDocumentCameraViewController) and return to sample app. But, In my case its not returning.
Any help much appreciated pls...
You add it as a child here
let scannerViewController = VNDocumentCameraViewController()
private func createTaskController(){
scannerViewController.delegate = self
self.clientView?.addChild(scannerViewController)
self.clientView?.view.addSubview(scannerViewController.view)
scannerViewController.didMove(toParent: clientView)
/// scannerViewController.dismiss(animated: true) remove this line
}
then to remove do
scannerViewController.removeFromParent()
scannerViewController.view.removeFromSuperView()
OR
private func createTaskController(){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion:nil)
}
Dismiss
controller.dismiss(animated: true)
To send the image create a function inside the clientView and call it
let newImage = compressedImage(originalImage)
self.clientView?.sendImage(newImage)

How can I assign a delegate for UIVideoEditorController in ReactNative when the module I am working on is just classes

func previewRecording (withFileName fileURL: String) {
if UIVideoEditorController.canEditVideo(atPath: fileURL) {
let rootView = UIApplication.getTopMostViewController()
let editController = UIVideoEditorController()
editController.videoPath = fileURL
// editController.delegate = ?????
rootView?.present(editController, animated: true, completion: nil)
} else {
}
}
^ current code
I have gone around the internet in circles a few times trying to figure this out. What would be the best approach for specifying a delegate for the UIVideoEditorController here? This is a react-native module where there is no ViewController, just utility classes.
Some simple example code I have come across
extension SomeViewController :
UIVideoEditorControllerDelegate,
UINavigationControllerDelegate {
func videoEditorController(_ editor: UIVideoEditorController,
didSaveEditedVideoToPath editedVideoPath: String) {
print("saved!")
}
}
I am just loss about how to make this happen in my module though.
#objc class ScreenRecordCoordinator: NSObject
{
let previewDelegateView = PreviewDelegateView()
// .......
editController.delegate = previewDelegateView
}
class PreviewDelegateView: UIViewController, UINavigationControllerDelegate, UIVideoEditorControllerDelegate {
var isSaved:Bool = false
func videoEditorController(_ editor: UIVideoEditorController, didSaveEditedVideoToPath editedVideoPath: String) {
print("save called")
if(!self.isSaved) {
self.isSaved = true
print("trimmed video saved!")
editor.dismiss(animated: true, completion: {
ReplayFileUtil.replaceItem(at: URL(fileURLWithPath: editor.videoPath), with: URL(fileURLWithPath: editedVideoPath))
self.isSaved = false
})
}
}
}

Logout functionality in swift

I am trying to provide in my app the logout functionality, I would like to know if this way is a good approach to continue.Classes involved are described below:
the first one is the AuthViewCoordinator, which class redirects to the user to auth screens
protocol AuthViewCoordinatorDelegate: class {
func authCompleted(coordinator: AuthViewCoordinator)
}
class AuthViewCoordinator: Coordinator {
weak var fromViewController: UIViewController?
weak var navigationController: UINavigationController?
weak var delegate: AuthViewCoordinatorDelegate?
init(fromViewController: UIViewController, delegate: AuthViewCoordinatorDelegate) {
self.fromViewController = fromViewController
self.delegate = delegate
}
func start() {
let authViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "AuthViewController") as! AuthViewController
authViewController.coordinator = self
let navigationController = NavigationController(rootViewController: authViewController)
navigationController.navigationBar.isHidden = true
fromViewController?.present(navigationController, animated: true, completion: nil)
self.navigationController = navigationController
}
func userDidSelectLogin() {
let loginViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
loginViewController.viewModel.coordinator = self
self.navigationController?.pushViewController(loginViewController, animated: true)
}
func userDidSelectSignUp() {
let signupViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "SignUpViewController") as! SignUpViewController
signupViewController.viewModel.coordinator = self
self.navigationController?.pushViewController(signupViewController, animated: true)
}
func userDidLogin() {
navigationController?.dismiss(animated: true, completion: nil)
self.delegate?.authCompleted(coordinator: self)
}
func userDidSignUp() {
navigationController?.dismiss(animated: true, completion: nil)
self.delegate?.authCompleted(coordinator: self)
}
}
And the 2nd one is an external class called SessionController. In this class I'm trying to call AuthViewCoordinator().start() inside the function logout immediately after the tokens have been removed to show again the auth screen to the user, but the output is
Use of unresolved identifier 'AuthViewCoordinator'
public class SessionController{
public enum SessionState {
case anonymous
case authenticated
case notAuthenticated
}
let service: Service
let sessionProvider: SessionProvider
convenience public init() {
self.init(service: Service.instance, sessionProvider: SessionProvider.instance)
}
init(service: Service, sessionProvider: SessionProvider) {
self.service = service
self.sessionProvider = sessionProvider
}
public func getMe(completion: #escaping (Error?) -> ()){
service.execute(resource: Login.getMe()) { (result) in
if let error = result.error {
completion(error)
} else if let session = result.value {
print("\n session \(session)\n")
completion(nil)
}
}
}
public func logout() {
self.sessionProvider.removeUserToken()
self.sessionProvider.removeInstanceToken()
self.sessionProvider.removeAnonymousToken()
AuthViewController().start()
}
public func state() -> SessionState {
if let _ = sessionProvider.getUserToken() {
print("###### authenticated #########")
return .authenticated
} else if let _ = sessionProvider.getAnonymousToken() {
print("###### anonymous #########")
return .anonymous
} else {
print("###### notAuthenticated #########")
return .notAuthenticated
}
}
}

Swift UIActivityViewController

Could anyone tell me how to implement "Open in Safari" in UIActivityViewController? I know this questions is a duplicate of another question posted a long time ago, and the method at that time was by using a framework that can no longer be used.
The data I am sharing is a URL. I already have a fully working ActivityVC and I only need to add that “open in safari” button.
Thank you very much.
code:
#IBAction func shareButtonPressed(_ sender: UIButton) {
let activityVC = UIActivityViewController(activityItems: [URL(string: urlStr)!], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
self.present(activityVC, animated: true, completion: nil)
}
You need to implement your own activity, please check the code below.
import UIKit
final class SafariActivity: UIActivity {
var url: URL?
override var activityImage: UIImage? {
return UIImage(named: "SafariActivity")!
}
override var activityTitle: String? {
return NSLocalizedString("Open in Safari", comment:"")
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
for item in activityItems {
if
let url = item as? URL,
UIApplication.shared.canOpenURL(url)
{
return true
}
}
return false
}
override func prepare(withActivityItems activityItems: [Any]) {
for item in activityItems {
if
let url = item as? URL,
UIApplication.shared.canOpenURL(url)
{
self.url = url
}
}
}
override func perform() {
var completed = false
if let url = self.url {
completed = UIApplication.shared.openURL(url)
}
activityDidFinish(completed)
}
}
let url = URL(string: "http://www.apple.com")!
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: [SafariActivity()])
present(activityViewController, animated: true, completion: nil)
Updated to Swift 5.1 & iOS 13
Bonus:
ActivityType extension to use with .excludedActivityTypes.
UIImage(systemName:) to use SF Symbols plus .applyingSymbolConfiguration to take advantage of its flexibility.
To improve:
Implement completion handler on UIApplication.shared.open to handle errors (unlikely to occur).
import UIKit
extension UIActivity.ActivityType {
static let openInSafari = UIActivity.ActivityType(rawValue: "openInSafari")
}
final class SafariActivity: UIActivity {
var url: URL?
var activityCategory: UIActivity.Category = .action
override var activityType: UIActivity.ActivityType {
.openInSafari
}
override var activityTitle: String? {
"Open in Safari"
}
override var activityImage: UIImage? {
UIImage(systemName: "safari")?.applyingSymbolConfiguration(.init(scale: .large))
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
activityItems.contains { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false }
}
override func prepare(withActivityItems activityItems: [Any]) {
url = activityItems.first { $0 is URL ? UIApplication.shared.canOpenURL($0 as! URL) : false } as? URL
}
override func perform() {
if let url = url {
UIApplication.shared.open(url)
}
self.activityDidFinish(true)
}
}
Try this Link if it meets your requirement
Link - https://bjartes.wordpress.com/2015/02/19/creating-custom-share-actions-in-ios-with-swift/
Code Required
class FavoriteActivity: UIActivity {
override func activityType() -> String? {
return "TestActionss.Favorite"
}
override func activityTitle() -> String? {
return "Add to Favorites"
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
NSLog("%#", __FUNCTION__)
return true
}
override func prepareWithActivityItems(activityItems: [AnyObject]) {
NSLog("%#", __FUNCTION__)
}
override func activityViewController() -> UIViewController? {
NSLog("%#", __FUNCTION__)
return nil
}
override func performActivity() {
// Todo: handle action:
NSLog("%#", __FUNCTION__)
self.activityDidFinish(true)
}
override func activityImage() -> UIImage? {
return UIImage(named: "favorites_action")
}
}
Usage
#IBAction func showAvc(sender: UIButton) {
let textToShare = "Look at this awesome website!"
let myWebsite = NSURL(string: "http://www.google.com/")!
let objectsToShare = [textToShare, myWebsite]
let applicationActivities = [FavoriteActivity()]
let avc = UIActivityViewController(activityItems: objectsToShare, applicationActivities: applicationActivities)
self.presentViewController(avc, animated: true, completion: nil)
}

Stripe - retrieveCustomer callback infinite loading

I had implement the Stripe to my project.I'm using an extension of default STPPaymentMethodsViewController like this:
class PaymentMethodVC: STPPaymentMethodsViewController {
convenience init()
{
let theme = STPTheme()
theme.primaryBackgroundColor = UIColor.pintHubDarkBrown
theme.secondaryBackgroundColor = UIColor.pintHubHeaderColor
theme.accentColor = UIColor.white
theme.primaryForegroundColor = UIColor.pintHubOrange
theme.secondaryForegroundColor = UIColor.pintHubOrange
theme.font = UIFont.mainRegular()
let paymentdelegate = PaymentMethodVCDelegate()
let paymentConfig = STPPaymentConfiguration.shared()
paymentConfig.publishableKey = "stripePublickToken"
let apiAdapter = PaymentApiAdapter()
self.init(configuration: paymentConfig, theme: theme, apiAdapter: apiAdapter, delegate: paymentdelegate)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
PaymentMethodVCDelegate is an object that implements STPPaymentMethodsViewControllerDelegate that methods are never called and
PaymentApiAdapter is other object that implements STPBackendAPIAdapter protocol which methods are:
public func retrieveCustomer(_ completion: #escaping Stripe.STPCustomerCompletionBlock)
public func attachSource(toCustomer source: STPSource, completion: #escaping Stripe.STPErrorBlock)
public func selectDefaultCustomerSource(_ source: STPSource, completion: #escaping Stripe.STPErrorBlock)
everything works fine expect when i want to return an error to the callback method func retrieveCustomer(_ completion: #escaping Stripe.STPCustomerCompletionBlock) that is a method of the STPBackendAPIAdapter protocol more details here.
this is my code:
func retrieveCustomer(_ completion: #escaping (STPCustomer?, Error?) -> Swift.Void)
{
stripeEndpoint.getStripeCustomer(for: "myStrypeCustomerId") { (status, JSON) in
if !status.success()
{
let userInfo = [NSLocalizedDescriptionKey:status.error,
NSLocalizedFailureReasonErrorKey: status.code,
NSLocalizedRecoverySuggestionErrorKey: ""
] as [String : Any]
let error = NSError(domain: "MyDomain", code: Int(status.error) ?? 0, userInfo: userInfo)
completion(nil, error)
}
else
{
var customer:STPCustomer? = nil
if let jsonData = JSON
{
let deserializer = STPCustomerDeserializer(jsonResponse: jsonData)
customer = deserializer.customer!
}
completion(customer, nil)
}
}
and when i receive an error the screen displays and infinite loading indicator.
and if i call completion(nil, nil) the loading disappear but i when i press cancel the ViewController don't pop from stack

Resources