AVCaptureVideoPreviewLayer not showing - objective-c

The goal is to simply display live video input from the webcam.
The code is distilled from one of Apple's examples and it seems to be at least partially working since the led next to the camera lights up when running.
I ctrl-dragged from the view in the storyboard to the outlet property in the interface and it is connected as referencing outlet now but nothing shows up, not even the black background color is set.
Any idea where I'm going wrong?
Thanks for your help
Edit:
Actually it is working just as expected if I set a breakpoint at the [previewViewLayer addSublayer:self.previewLayer]; line and continue to run in the debugger. There has to be some kind of timing issue.
Interface:
#property (retain) AVCaptureDevice *videoDevice;
#property (retain) AVCaptureDeviceInput *videoDeviceInput;
#property (retain) AVCaptureSession *session;
#property (retain) AVCaptureVideoPreviewLayer *previewLayer;
#property (assign) IBOutlet NSView *previewView;
Implementation:
- (void)viewDidLoad {
[super viewDidLoad];
[self getDevice];
[self initSession];
}
- (void)initSession {
// Create a capture session
// self.session = [[AVCaptureSession alloc] init];
[self setSession:[[AVCaptureSession alloc] init]];
// Attach preview to session
CALayer *previewViewLayer = [self.previewView layer];
[previewViewLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
[self.previewLayer setFrame:[previewViewLayer bounds]];
[self.previewLayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable];
[previewViewLayer addSublayer:self.previewLayer];
// Start the session
[self.session startRunning];
// Create a device input for the device and add it to the session
NSError *error = nil;
AVCaptureDeviceInput *newVideoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.videoDevice error:&error];
[self.session addInput:newVideoDeviceInput];
self.videoDeviceInput = newVideoDeviceInput;
[self.session commitConfiguration];
}
- (void)getDevice {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
NSLog(#"Device name: %#", [device localizedName]);
self.videoDevice = device;
}
}

Solved:
I moved [self initSession];from - (void)viewDidLoadto - (void)viewDidLayout
Update:
I ran into more problems later with - (void)viewDidLayout so eventually I moved to - (void)viewDidAppear

Related

Estimote: didRangeBeacons not called when in background?

i was wondering why I don't get the notification if my beacon proximity is Immediate – when the app is in background. In foreground mode, everything works fine.
#import "ESTViewController.h"
#import <ESTBeaconManager.h>
#interface ESTViewController () <ESTBeaconManagerDelegate>
#property (nonatomic, strong) ESTBeaconManager* beaconManager;
#property (nonatomic, strong) ESTBeacon* selectedBeacon;
#property (weak, nonatomic) IBOutlet UILabel *outputLabel;
#end
#implementation ESTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
/////////////////////////////////////////////////////////////
// setup Estimote beacon manager
// craete manager instance
self.beaconManager = [[ESTBeaconManager alloc] init];
self.beaconManager.delegate = self;
self.beaconManager.avoidUnknownStateBeacons = YES;
// create sample region with major value defined
ESTBeaconRegion* region = [[ESTBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID identifier: #"EstimoteSampleRegion" ];
NSLog(#"TODO: Update the ESTBeaconRegion with your major / minor number and enable background app refresh in the Settings on your device for the NotificationDemo to work correctly.");
// start looking for estimote beacons in region
[self.beaconManager startMonitoringForRegion:region];
[self.beaconManager startRangingBeaconsInRegion:region];
[self.beaconManager requestStateForRegion:region];
// setup view
self.outputLabel.text = #"Seachring for reagion";
}
-(void)beaconManager:(ESTBeaconManager *)manager
didDetermineState:(CLRegionState)state
forRegion:(ESTBeaconRegion *)region
{
// NSLog(#"Region: %#", region);
if(state == CLRegionStateInside)
{
NSLog(#"State: CLRegionStateInside");
}
else
{
NSLog(#"State: CLRegionOutside");
}
}
-(void)beaconManager:(ESTBeaconManager *)manager
didRangeBeacons:(NSArray *)beacons
inRegion:(ESTBeaconRegion *)region
{
if([beacons count] > 0)
{
self.selectedBeacon = [beacons firstObject]; // get the closest
NSString* labelText = [NSString stringWithFormat:
#"Major: %i, Minor: %i\nRegion: ",
[self.selectedBeacon.major unsignedShortValue],
[self.selectedBeacon.minor unsignedShortValue]];
// calculate and set new y position
switch (self.selectedBeacon.proximity)
{
case CLProximityUnknown:
labelText = [labelText stringByAppendingString: #"Unknown"];
break;
case CLProximityImmediate:
labelText = [labelText stringByAppendingString: #"Immediate"];
[self fireNotification];
break;
case CLProximityNear:
labelText = [labelText stringByAppendingString: #"Near"];
break;
case CLProximityFar:
labelText = [labelText stringByAppendingString: #"Far"];
break;
default:
break;
}
self.outputLabel.text = labelText;
}
}
-(void)fireNotification {
NSLog(#"Fire Notification from background");
// present local notification
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = #"You are very close to the beacon";
notification.soundName = UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
// Request a server...
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
With iOS, ranging for iBeacons generally only works in the foreground. The code that checks for CLProximityImmediate is in a ranging callback method.
If your app is in the background, it will only receive ranging callbacks for five seconds after entering/exiting a monitored iBeacon region.
This principle is true for the standard iOS CoreLocation APIs. While you are using the proprietary Estimote SDK, the same limitations also apply since it is a wrapper around the standard APIs.

MapKit: annotation views don't display until scrolling the map

I expect once clicking the toolbar button like "cafe", cafes will show in the visible map region. Data are fetched from Google Places API. Everything works fine except pins don't display after clicking the button.
A latency is expected here for sure. Yet i do the fetch in a background queue and puts up a spinning wheel while waiting, and the spinning wheel hides when fetching and parsing is done. So I am quite sure the data is there at the moment that spinning wheel disappears. But the pins don't show up until I scroll the map.
I figure that scrolling map only triggers mapView:regionDidChangeAnimated:. But I can't figure out how it relates the problem. Can anyone help?
The source code of ViewController.m, where pretty much everything happens.
#import "ViewController.h"
#import "MapPoint.h"
#import "MBProgressHUD.h"
#define kGOOGLE_API_KEY #"AIzaSyCHqbAoY7WCL3l7x188ZM4ciiTixejzQ4Y"
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#interface ViewController () <CLLocationManagerDelegate, MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) CLLocationManager *locationManager;
#property int currentDist;
#property CLLocationCoordinate2D currentCentre;
#end
#implementation ViewController
#synthesize mapView = _mapView;
#synthesize locationManager = _locationManager;
#synthesize currentDist = _currentDist;
#synthesize currentCentre = _currentCentre;
- (void)viewDidLoad{
[super viewDidLoad];
}
- (void)viewDidUnload{
[self setMapView:nil];
[super viewDidUnload];
}
// set the map region after launching
-(void)viewWillAppear:(BOOL)animated
{
//Instantiate a location object.
self.locationManager = [[CLLocationManager alloc] init];
//Make this controller the delegate for the location manager.
self.locationManager.delegate = self;
//Set some parameters for the location object.
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
// order: latitude(纬度), longitude(经度)
CLLocationCoordinate2D center = self.locationManager.location.coordinate;
// 单位是degree
MKCoordinateSpan span = MKCoordinateSpanMake(0.03, 0.03);
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
[self.mapView setRegion:region animated:YES];
// NSLog(#"currentCentre is (%f , %f)", self.currentCentre.latitude, self.currentCentre.longitude);
}
// Get place tpye from button title
// All buttons share this one method
- (IBAction)toolbarButtonPressed:(id)sender
{
UIBarButtonItem *button = (UIBarButtonItem *)sender;
NSString *buttonTitle = [button.title lowercaseString];
//Use this title text to build the URL query and get the data from Google.
[self queryGooglePlaces:buttonTitle];
}
// Parse response JSON data
-(void)parseData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
//The results from Google will be an array obtained from the NSDictionary object with the key "results".
NSArray* places = [json objectForKey:#"results"];
[self plotPositions:places];
NSLog(#"Plot is done");
}
// Format query string
-(void) queryGooglePlaces: (NSString *) googleType {
// query string
NSString *url = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/search/json?location=%f,%f&radius=%#&types=%#&sensor=true&key=%#", self.currentCentre.latitude, self.currentCentre.longitude, [NSString stringWithFormat:#"%i", _currentDist], googleType, kGOOGLE_API_KEY];
//string to URL
NSURL *googleRequestURL=[NSURL URLWithString:url];
// Retrieve data from the query URL by GCD
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: googleRequestURL];
[self parseData:data];
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Please Wait..";
}
#pragma mark - Map View Delegate
// called many times when map scrolling or zooming
// Use this to get currentCentre and currentDist (radius)
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
//Get the east and west points on the map so you can calculate the distance (zoom level) of the current map view.
MKMapRect mRect = self.mapView.visibleMapRect;
MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect));
MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect));
//Set your current distance instance variable.
self.currentDist = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint);
//Set your current center point on the map instance variable.
self.currentCentre = self.mapView.centerCoordinate;
// NSLog(#"currentCentre is (%f , %f)", self.currentCentre.latitude, self.currentCentre.longitude);
}
// Setup annotation objects
-(void)plotPositions:(NSArray *)data {
// 1 - Remove any existing custom annotations but not the user location blue dot.
for (id<MKAnnotation> annotation in self.mapView.annotations) {
if ([annotation isKindOfClass:[MapPoint class]]) {
[self.mapView removeAnnotation:annotation];
}
}
// 2 - Loop through the array of places returned from the Google API.
for (int i=0; i<[data count]; i++) {
//Retrieve the NSDictionary object in each index of the array.
NSDictionary* place = [data objectAtIndex:i];
// 3 - There is a specific NSDictionary object that gives us the location info.
NSDictionary *geo = [place objectForKey:#"geometry"];
// Get the lat and long for the location.
NSDictionary *loc = [geo objectForKey:#"location"];
// 4 - Get your name and address info for adding to a pin.
NSString *name=[place objectForKey:#"name"];
NSString *vicinity=[place objectForKey:#"vicinity"];
// Create a special variable to hold this coordinate info.
CLLocationCoordinate2D placeCoord;
// Set the lat and long.
placeCoord.latitude=[[loc objectForKey:#"lat"] doubleValue];
placeCoord.longitude=[[loc objectForKey:#"lng"] doubleValue];
// 5 - Create a new annotation.
MapPoint *placeObject = [[MapPoint alloc] initWithName:name address:vicinity coordinate:placeCoord];
[self.mapView addAnnotation:placeObject];
}
NSLog(#"addAnnotation is done");
}
// Setup annotation view
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
// Define your reuse identifier.
static NSString *identifier = #"MapPoint";
if ([annotation isKindOfClass:[MapPoint class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.animatesDrop = YES;
// NSLog(#"annotation view is added");
return annotationView;
}
return nil;
}
#end
A couple of things:
Move your removeAnnotation and addAnnotation code to run on the UI thread, e.g.:
dispatch_async (dispatch_get_main_queye(), ^
{
[self.mapView addAnnotation:placeObject];
});
Move your viewWillAppear initialization code to viewDidLoad. viewWillAppear may be called multiple times during the lifetime of your view controller

AVAudioPlayer not playing iPad2

New to IOS dev, I'm testing AVAudioplayer to play sound on iPad2 (Xcode 4.2 project, ARC/storyboard enabled). Sound plays ok in simulator and no error. No error on device either but no sound.
Been browsing this fine resource temple, but nothing I've tried based on feedback here has produced anything but deafening iPad silence. Could someone help? My .h:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController
<AVAudioPlayerDelegate>
{
AVAudioPlayer *audioPlayer;
UISlider *volumeControl;
UILabel *timerLabel;
NSTimer *playbackTimer;
}
#property (nonatomic, retain) IBOutlet UISlider *volumeControl;
#property (nonatomic, retain) IBOutlet UILabel *timerLabel;
#property (nonatomic, retain) NSTimer *playbackTimer;
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
-(IBAction) playAudio;
-(IBAction) stopAudio;
-(IBAction) adjustVolume;
#end
My .m:
#import "ViewController.h"
#implementation ViewController
#synthesize volumeControl, timerLabel, playbackTimer, audioPlayer;
-(void)playAudio
{
playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
[audioPlayer play];
}
-(void)stopAudio
{
[playbackTimer invalidate];
[audioPlayer stop];
}
-(void)adjustVolume
{
if (audioPlayer != nil)
{
audioPlayer.volume = volumeControl.value;
}
}
-(void)updateTime
{
float minutes = floor(audioPlayer.currentTime/60);
float seconds = audioPlayer.currentTime - (minutes * 60);
float duration_minutes = floor(audioPlayer.duration/60);
float duration_seconds =
audioPlayer.duration - (duration_minutes * 60);
NSString *timeInfoString = [[NSString alloc]
initWithFormat:#"%0.0f.%0.0f / %0.0f.%0.0f",
minutes, seconds,
duration_minutes, duration_seconds];
timerLabel.text = timeInfoString;
}
-(void)audioPlayerDidFinishPlaying:
(AVAudioPlayer *)player successfully:(BOOL)flag
{
}
-(void)audioPlayerDecodeErrorDidOccur:
(AVAudioPlayer *)player error:(NSError *)error
{
}
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
}
my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"song"
ofType:#"mp3"]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
}
[super viewDidLoad];
}
Make sure that the file is indeed an mp3 format. Make sure that you are copying the file into the bundle, and not playing off of local path on your desktop. Check the device volume. Check the return BOOL from the play call. all of these are possible explanations.
Is it not playing sounds at all? No sounds even with headphones plugged in? If it's just no sound through the built-in speaker, but sounds through headphones, make sure that your device ring/sounds volume isn't muted. Check the toggle switch on the side (if you have that set to mute vs orientation lock). The bell shouldn't be crossed out. Just because you press the volume up button doesn't mean it's unmuted from the speaker. Have you tested YouTube videos or music files to ensure your iPad isn't having hardware issues?

After setting UITextView's text property, screen does not update accordingly

I have view controllers A(FileListViewController) and B(TextFileViewController). A is a UITableViewController. What I am doing now is that after selecting a row in controller A, I load a text file and display that text in a UITextView in controller B.
The following is the header and implementation part(some code is abridged) of my the two controllers.
FileListViewcontroller Interface:
#interface FileListViewController : UITableViewController {
NSMutableArray * fileList;
DBRestClient* restClient;
TextFileViewController *tfvc;
}
#property (nonatomic, retain) NSMutableArray * fileList;
#property (nonatomic, retain) TextFileViewController *tfvc;
#end
FileListViewController Implementation:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DBMetadata *metaData = [fileList objectAtIndex:indexPath.row];
if(!metaData.isDirectory){
if([Utils isTextFile:metaData.path]){
if(!tfvc){
tfvc = [[TextFileViewController alloc] init];
}
[self restClient].delegate = self;
[[self restClient] loadFile:metaData.path intoPath:filePath];
[self.navigationController pushViewController:tfvc animated:YES];
}
}
- (void)restClient:(DBRestClient*)client loadedFile:(NSString*)destPath {
NSError *err = nil;
NSString *fileContent = [NSString stringWithContentsOfFile:destPath encoding:NSUTF8StringEncoding error:&err];
if(fileContent) {
[tfvc updateText:fileContent];
} else {
NSLog(#"Error reading %#: %#", destPath, err);
}
}
And here is the interface for TextFileViewController:
#interface TextFileViewController : UIViewController {
UITextView * textFileView;
}
#property (nonatomic, retain) IBOutlet UITextView * textFileView;
-(void) updateText:(NSString *) newString;
#end
TextFileViewController implementation:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(done)] autorelease];
textFileView = [[UITextView alloc] init];
}
- (void) updateText:(NSString *)newString {
NSLog(#"new string has value? %#", newString);
[textFileView setText:[NSString stringWithString:newString]];
NSLog(#"print upddated text of textview: %#", textFileView.text);
[[self textFileView] setNeedsDisplay];
}
(void)restClient: loadedFile: will be call after the loadFile:intoPath: is completed in the disSelectRowAtIndexPath method.
In TextFileViewController's updateText method, from NSLog I see that the text property is updated correctly. But the screen does not update accordingly. I've tried setNeedsDisplay but in vain. Did I miss something?
Thanks in advance.
In -[TextFileViewController viewDidLoad] you're creating a UITextView, but its frame is never set, and it's not added to the view hierarchy.
Try changing this:
textFileView = [[UITextView alloc] init];
to this:
textFileView = [[UITextView alloc] initWithFrame:[[self view] bounds]];
[[self view] addSubview:textFileView];
The problem is that textFileView is created in the viewDidLoad method of TextFileViewController. This method has not yet been called by the time you call updateText (this happens before the TextFileViewController is pushed).
You can fix this by forcing the view to load before you call [[self restClient] loadFile:metaData.path intoPath:filePath];.

Objective-C: Getting UIActivity indicators for images in UITableView

I was wondering if anyone knew the best way of getting in a UITableView, where we have images on the left hand side, for the images to appear with UIActivity indicator gradually rather than not being there and appearing all of a sudden. Ive seen that on a few apps and wanted to know how to do it.
I haven't done this myself, but my idea would be to create my own UIView subclass that handles showing the UIActivity indicator while the image is loading. Then add instances of that view to your table cells instead of plain UIImageViews.
Update:
OK, as I said before, I haven't done this myself. The following is just typed into the browser, and I'm not sure if you'd run into any issues with threading, but it should give you an idea as to how to approach this:
#interface IndicatorImageView : UIView {
UIImageView *imageView;
UIActivityIndicatorView *indicator;
NSString *imageName;
NSURL *imageURL;
}
#property (nonatomic, copy) NSString *imageName;
#property (nonatomic, retain) NSURL* imageURL;
#end
#implementation IndicatorImageView
#synthesize imageName;
#synthesize imageURL;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
imageView = [[UIImageView alloc] initWithFrame:self.bounds];
[self addSubview:imageView];
indicator = [[UIActivityIndicatorView alloc] initWithFrame:self.bounds];
[self addSubview:indicator];
}
return self;
}
- (void)setImageName:(NSString *)anImageName {
if (imageName == anImageName) {
return;
}
[imageName release];
imageName = [anImageName copy];
// optionally remove the old image while we're updating
//imageView.image = nil;
[indicator startAnimating];
[self performSelectorInBackground:#selector(updateImageViewUsingImageName) withObject:nil];
}
- (void)setImageURL:(NSURL *)anImageURL {
if (imageURL == anImageURL) {
return;
}
[imageURL release];
imageURL = [anImageURL copy];
// optionally remove the old image while we're updating
//imageView.image = nil;
[indicator startAnimating];
[self performSelectorInBackground:#selector(updateImageViewUsingImageURL) withObject:nil];
}
- (void)updateImageViewUsingImageName {
NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
imageView.image = [UIImage imageNamed:imageName];
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil];
[pool release];
}
- (void)updateImageViewUsingImageURL {
NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
imageView.image = [UIImage imageWithData:imageData];
[indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil];
[pool release];
}
#end
When you set the imageName property, the image will be loaded in the background while the UIActivityIndicatorView is animating. Once the image is loaded and set on the UIImageView, we stop animating the activity indicator.
If I can remember correctly something like that was shown in stanford lectures cs193, iPhone Application Programming, when loading profile pictures from twitter accounts.
Have a look for the appropriate presentation (I can't remember which one it was), look at notes that talk about table view then have a look at itunesu video of the lecture.
http://www.stanford.edu/class/cs193p/cgi-bin/index.php
Subclass UIImage and add the UIActivityIndicator as a subview. Add some methods to the class for setting the image source, and start/stop the UIActivityIndicator appropriately.