I want to capture a picture from the camera with no preview.
Though I am able to do that with a UIViewController with the help of AVFoundation framework.
But now I have to do the same without the ViewController.
#Issue
When I trying to do the same with a NSObject class the AVCapturePhotoCaptureDelegate is not called.
TestingCamera.h
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface TestingCamera : NSObject <AVCapturePhotoCaptureDelegate> {
AVCaptureSession *captureSession;
AVCapturePhotoOutput *photoOutput;
AVCapturePhotoSettings *photoSetting;
AVCaptureConnection *captureConnection;
}
#property(nonatomic, assign) id <AVCapturePhotoCaptureDelegate> delegate;
- (void) takePictureWithNoPreviewFrontFacingCamera;
#end
NS_ASSUME_NONNULL_END
TestingCamera.m
#import "TestingCamera.h"
#implementation TestingCamera
- (void) initCaptureSession {
captureSession = [[AVCaptureSession alloc] init];
if([captureSession canSetSessionPreset: AVCaptureSessionPresetPhoto] ) {
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
}
[captureSession startRunning];
}
-(void) setNewPhotoSetting {
photoSetting = [AVCapturePhotoSettings photoSettingsWithFormat:#{AVVideoCodecKey : AVVideoCodecTypeJPEG}];
[photoOutput setPhotoSettingsForSceneMonitoring:photoSetting];
}
-(void) takePictureWithNoPreviewFrontFacingCamera {
[self initCaptureSession];
[self initInputDevice:[self frontFacingCameraIfAvailable]];
[self initOuput];
[self setNewPhotoSetting];
captureConnection = nil;
for (AVCaptureConnection *connection in photoOutput.connections) {
for (AVCaptureInputPort *port in [connection inputPorts]) {
if ([[port mediaType] isEqual: AVMediaTypeVideo]) {
captureConnection = connection;
break;
}
}
if (captureConnection) {
break;
}
}
[photoOutput capturePhotoWithSettings:photoSetting delegate:self];
}
-(void) initInputDevice: (AVCaptureDevice *) inputDevice {
AVCaptureDeviceInput *deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputDevice error:nil];
if ([captureSession canAddInput:deviceInput]) {
[captureSession addInput:deviceInput];
}
}
-(void) initOuput {
photoOutput = [[AVCapturePhotoOutput alloc] init];
if ([captureSession canAddOutput:photoOutput]) {
[captureSession addOutput:photoOutput];
}
}
-(AVCaptureDevice *) frontFacingCameraIfAvailable {
AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:#[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
NSArray *captureDevices = [captureDeviceDiscoverySession devices];
if (!captureDevices || !captureDevices[0]){
return [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
} else {
return captureDevices[0];
}
}
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(nullable NSError *)error
{
NSData *imageData = [photo fileDataRepresentation];
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSLog(#"Encoded: %#", base64);
}
#end
I am calling the method from another ViewController like this
- (IBAction) testPicture:(id)sender {
TestingCamera *noPreviewCamera = [[TestingCamera alloc] init];
[noPreviewCamera takePictureWithNoPreviewFrontFacingCamera];
}
Im implementing GCDAsyncSocket delegate in NSObject class, then call it in viewController.
When running it, i found 'didConnectToHost' and 'didWriteDataWithTag' is called, but i didn't see 'didReadData' called. Here is my code:
SocketUtils.h
#interface SocketUtils : NSObject <GCDAsyncSocketDelegate>
#property (nonatomic) void (^SocketCallback)(id data);
#property (nonatomic) NSString *message;
#property (nonatomic) UIViewController *vc;
+ (SocketUtils *) createSocket:(NSString *)message inViewController:(UIViewController *)vc;
- (void) connectToSocket;
#end
SocketUtils.m
#implementation SocketUtils{
GCDAsyncSocket *socket;
}
+ (SocketUtils *) createSocket:(NSString *)message inViewController:(UIViewController *)vc{
SocketUtils *this = [[self alloc] init];
this.message = message;
[this connectToSocket];
return this;
}
- (void)connectToSocket{
socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *err = nil;
if (![socket connectToHost:SOCKET_IP onPort:[SOCKET_PORT intValue] error:&err])
{
NSLog(#"err: %#", [err localizedDescription]);
if(err){
[Utils showErrorDialog:[err localizedDescription] atViewController:_vc];
}
return;
}
}
#pragma mark - Socket delegate
- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(#"Cool,didConnectToHost: %#", host);
[socket writeData:[_message dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[socket readDataWithTimeout:-1 tag:0];
NSLog(#"didWriteDataWithTag: %ld", tag);
}
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
NSLog(#"didReadData : %#", data);
NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length])];
NSString *msg = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
_SocketCallback(msg);
}
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock{
NSLog(#"socketDidCloseReadStream");
}
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
NSLog(#"disconnected to socket: %#", err);
[Utils showErrorDialog:[err localizedDescription] atViewController:_vc];
}
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
NSLog(#"Accepted new socket from %#:%hu", [newSocket connectedHost], [newSocket connectedPort]);
}
Then in viewController i called:
socketUtils = [SocketUtils createSocket:#"GET_BOARD_CODE" inViewController:self];
socketUtils.SocketCallback = ^(id data) {
doSth();
};
The first thing that I predict is the destroying of this class, but i try to keep all #property = strong, but not work.
Can anyone help?
after many times to search, i realized that i forgot to append "\r\n" to message. Okey just change 1 line of code to:
this.message = [message stringByAppendingString:#"\r\n"];
And it work as expect.
#import "CacheManager.h"
#implementation CacheManager
//Get an Instance of Cache Manager Class
+(instancetype) getInstance {
static dispatch_once_t once;
static CacheManager *sinstance=nil;
if(sinstance==nil){
dispatch_once(&once, ^{
sinstance = [[CacheManager alloc]init];
});
}
return sinstance;
}
#import "Resturant.h"
#implementation Resturant
-(instancetype) initWithDictionary:(NSDictionary*)dictionary {
self = [super init];
if (self && dictionary) {
self.iD=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_RESURANT_ID];
self.name=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_RESTURANT_NAME];
self.url=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_RESTURANT_URL];
self.photoURL=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_RESTURANT_PHOTO_URL];
self.rating=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_RESTURANT_USER_RATING_KEY][KZOMATO_AGGREGATE_RATTING];
self.adress=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_LOCATION_KEY][KZOMATO_ADRESS_KEY];
self.averageCostForTwo=dictionary[KZOMATO_RESTURANT_KEY][KZOMATO_AVERAGE_COST_KEY];
self.image = nil;
self.imageLoaded = FALSE;
[self getImage:^(UIImage *image) {
self.image=image;
}];
return self;
}
return nil;
}
//method to get image from Cached Data
-(void) getImage :(void (^)(UIImage * image))callBack {
// This line of code getInstance is showing error
[[CacheManager getInstance]imageForURL:self.photoURL withCompletionHandler:^(UIImage *image) {
self.imageLoaded = TRUE;
callBack(image);
}];
}
#end
I started with xmppframework recently,but i am stuck with an issue.i am able to connect to my server on my local network but the xmppstreamdelegate methods are not getting called on my custom class,but works absolutely fine on appdelegate class .Can anyone pls help me on this.Is the delegate only supported on the appdelegate class ?
Header:
#interface XmppClass : NSObject<XMPPStreamDelegate>{
XMPPStream *xmppStream;
Login * loginDetail;
BOOL allowSelfSignedCertificates;
BOOL allowSSLHostNameMismatch;
}
#property (nonatomic, strong, readonly) XMPPStream *xmppStream;
#property (nonatomic, strong) Login *loginDetail;
- (id)initWithLogin:(Login *) loginrefernce;
- (BOOL)connect;
- (void)disconnect;
- (void)setupStream;
#end
Implementation:
#implementation XmppClass
#synthesize xmppStream;
#synthesize loginDetail;
- (id)initWithLogin:(Login *) loginrefernce
{
self = [super init];
if (self) {
self.loginDetail=loginrefernce;
[DDLog addLogger:[DDTTYLogger sharedInstance]];
[self setupStream];
[self connect];
}
return self;
}
- (void)setupStream
{
NSAssert(xmppStream == nil, #"Method setupStream invoked multiple times");
// Setup xmpp stream
//
// The XMPPStream is the base class for all activity.
// Everything else plugs into the xmppStream, such as modules/extensions and delegates.
xmppStream = [[XMPPStream alloc] init];
#if !TARGET_IPHONE_SIMULATOR
{
// Want xmpp to run in the background?
//
// P.S. - The simulator doesn't support backgrounding yet.
// When you try to set the associated property on the simulator, it simply fails.
// And when you background an app on the simulator,
// it just queues network traffic til the app is foregrounded again.
// We are patiently waiting for a fix from Apple.
// If you do enableBackgroundingOnSocket on the simulator,
// you will simply see an error message from the xmpp stack when it fails to set the property.
xmppStream.enableBackgroundingOnSocket = YES;
}
#endif
NSLog(#"setup stream");
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppStream setHostName:#"10.68.202.123"];
//[xmppStream setHostPort:8070];
allowSelfSignedCertificates = NO;
allowSSLHostNameMismatch = NO;
// You may need to alter these settings depending on the server you're connecting to
}
- (BOOL)connect
{
NSLog(#"connect");
if (![xmppStream isDisconnected]) {
return YES;
}
//
// If you don't want to use the Settings view to set the JID,
// uncomment the section below to hard code a JID and password.
//
// myJID = #"user#gmail.com/xmppframework";
// myPassword = #"";
if (self.loginDetail.emailId == nil || self.loginDetail.password == nil) {
return NO;
}
[xmppStream setMyJID:[XMPPJID jidWithString:[self.loginDetail.emailId stringByAppendingString:#"/pc"]]];
NSError *error = nil;
if (![xmppStream connect:&error])
{
NSLog(#"Error connecting: %#", error);
return NO;
}
return YES;
}
- (void)disconnect
{
[xmppStream disconnect];
}
- (void)xmppStream:(XMPPStream *)sender socketDidConnect:(GCDAsyncSocket *)socket
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStream:(XMPPStream *)sender willSecureWithSettings:(NSMutableDictionary *)settings
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"some security thing");
if (allowSelfSignedCertificates)
{
[settings setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCFStreamSSLAllowsAnyRoot];
}
if (allowSSLHostNameMismatch)
{
[settings setObject:[NSNull null] forKey:(NSString *)kCFStreamSSLPeerName];
}
else
{
// Google does things incorrectly (does not conform to RFC).
// Because so many people ask questions about this (assume xmpp framework is broken),
// I've explicitly added code that shows how other xmpp clients "do the right thing"
// when connecting to a google server (gmail, or google apps for domains).
NSString *expectedCertName = nil;
NSString *serverDomain = xmppStream.hostName;
NSString *virtualDomain = [xmppStream.myJID domain];
if ([serverDomain isEqualToString:#"talk.google.com"])
{
if ([virtualDomain isEqualToString:#"gmail.com"])
{
expectedCertName = virtualDomain;
}
else
{
expectedCertName = serverDomain;
}
}
else if (serverDomain == nil)
{
expectedCertName = virtualDomain;
}
else
{
expectedCertName = serverDomain;
}
if (expectedCertName)
{
[settings setObject:expectedCertName forKey:(NSString *)kCFStreamSSLPeerName];
}
}
}
- (void)xmppStreamDidSecure:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"connected");
NSError *error = nil;
if (![[self xmppStream] authenticateWithPassword:self.loginDetail.password error:&error])
{
DDLogError(#"Error authenticating: %#", error);
}
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"authenticated");
}
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"did not authenticate");
}
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
return NO;
}
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
// A simple example of inbound message handling.
}
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{
DDLogVerbose(#"%#: %# - %#", THIS_FILE, THIS_METHOD, [presence fromStr]);
}
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
}
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{
DDLogVerbose(#"%#: %#", THIS_FILE, THIS_METHOD);
NSLog(#"%#",error);
}
#end
Most probably the problem is that your class instance, that is a delegate for your XMPPStream is released before the delegate method is called on it. Make it more persistent by making this class a property or instance variable of other class or by using dispatch_once. For example,
Change
YourClass *instance = [[YourClass alloc] init];
instance.xmppStream = ....
by
#property(nonatomic, strong) YourClass *instance;
self.instance = [[YourClass alloc] init];
self.instance.xmppStream = ....
Here YourClass contains XMPPStream and is delegate for it.
I've written a big blog post on this problem. This is pretty common situation.
http://blog.alwawee.com/2013/07/31/on-xmppframework-delegate-method-not-being-called/
you can use the multicast,sth like this
[[self appDelegate].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
I had same problem... this helped me. Hope it works for you, just make following Changes in your code.
Static XmppClass *selfRef = nil;
#implementation XmppClass
#synthesize xmppStream;
- (void)setupStream
{
// Setup xmpp stream
selfRef = [[XmppClass alloc] init];
xmppStream = [[XMPPStream alloc] init];
[xmppStream addDelegate:selfRef delegateQueue:dispatch_get_main_queue()];
#interface Tester()
{
int currentAccelerationOnYaxis;
}
#end
#implementation Tester
-(void) test
{
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
currentAccelerationOnYaxis = motion.userAcceleration.y;
}
];
while(1==1)
{
NSLog(#"current acceleration is: %f", currentAccelerationOnYaxis);
}
}
#end
I then execute the above method on a background thread like this :
[myTester performSelectorInBackground:#selector(test) withObject:nil];
and it works fine.
However, the following configuration is not working and I can't figure out why :
#implementation MotionHandler
#synthesize accelerationOnYaxis; // this is an int property of the MotionHandler class
-(void) startAccelerationUpdates
{
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
self.accelerationOnYaxis = motion.userAcceleration.y;
}
];
}
#implementation Tester
-(id)init
{
//...
currentMotionHandler = [[MotionHandler alloc] init];
}
-(void) test
{
[currentMotionHandler startAccelerationUpdates];
while(1==1)
{
NSLog(#"current acceleration is: %f", currentMotionHandler.accelerationOnYaxis);
}
}
#end
I then execute the above method on a background thread like this :
[myTester performSelectorInBackground:#selector(test) withObject:nil];
and it's not working, why is that ?
I figured it out. In my 2nd version the CMMotionManager instance I was creating got lost for some reason. Therefore, I changed the implementation of my MotionHandler class into this :
MotionHandler.m
#interface MotionHandler()
{
//..
CMMotionManager *motionManager; // I now declare it here
}
#implementation MotionHandler
#synthesize accelerationOnYaxis; // this is an int property of the MotionHandler class
-(void) startAccelerationUpdates
{
motionManager = [[CMMotionManager alloc] init]; // and then initialize it here..
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
self.accelerationOnYaxis = motion.userAcceleration.y;
}
];
}
-(void)test
{
[self startAccelerationUpdates];
while(1==1)
{
NSLog(#"current acceleration on y-axis is: %f", self.accelerationOnYaxis);
}
}
and now it seems to be working fine.