In .m file i have methods like
-(void) textdata
{
long i = search.text.length;
if (i > 0) {
searchButton.enabled = YES;
}
else
{
searchButton.enabled = NO;
}
[self buttonstate];
}
-(int) buttonstate
{
if ([searchButton isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
- (void) textFieldDidChange
{
[self textdata];
NSLog(#"test data is %#",search.text);
}
And in the tests.m file i have test like
-(void) testwithoutData
{
myapiViewController *apiViw = [[myapiViewController alloc]init];
[apiViw textdata];
int kn = [apiViw buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
You have to do alloc init because due to this instance variable getting nil. Try this -
In ViewController.m -
-(int) buttonstate {
long i = self.searchTxt.text.length;
if (i > 0) {
self.searchBtn.enabled = YES;
}
else
{
self.searchBtn.enabled = NO;
}
if ([self.searchBtn isEnabled]) {
j = 1;
}
else
j = 0;
NSLog(#" j value is %d",j);
return j;
}
In ViewControllerTests.m -
- (void)testExample {
// This is an example of a functional test case.
XCTAssert(YES, #"Pass");
self.vcToTest.searchTxt = [[UITextField alloc] init];
self.vcToTest.searchBtn = [[UIButton alloc] init];
self.vcToTest.searchTxt.text = #"test";
int kn = [self.vcToTest buttonstate];
NSLog(#"the value is %ld",(long)kn);
}
Check the attached screenshot output -
Related
I have an array of dictionary like below
self->arrField: (
{
fieldname = "Have you ever had any one of the following:";
fieldvalue = (
Diabetes,
Hypertension
);
tagvalue = 0;
},
{
fieldname = "By continuing with this survey, you agree to our terms of use and Parsons Information Governance and Privacy Policies, Procedures and Standards, available on the Corporate Policy Center.";
fieldvalue = Agree;
tagvalue = 1;
},
{
fieldname = "Have you tested positive for COVID-19 in the last 10 days?";
fieldvalue = No;
tagvalue = 4;
},
{
fieldname = "Do you have any known heart conditions or on any medications that elevate your heart rate";
fieldvalue = Yes;
tagvalue = 6;
},
{
fieldname = "Have you received a COVID-19 test in the past 48 hours";
fieldvalue = Yes;
tagvalue = 7;
},
{
fieldname = "In the last 48 hours have you experienced any of these symptoms";
fieldvalue = (
cough,
fever
);
tagvalue = 10;
}
)
So if the user changes any of the fieldvalue I need to replace that array, I have tried using replaceObjectAtIndex but it will not work out because I need compare the string.
Below is my implementation:
-(void)ChkUnChk:(UIButton*)sender {
dispatch_async(dispatch_get_main_queue(), ^{
UIButton *btn=(UIButton *)sender;
NSString *strValue = btn.accessibilityHint;
NSString *Str=[NSString stringWithFormat:#"%d",(int)btn.tag];
int iTag = [[sender.layer valueForKey:#"anyKey"] intValue];
// new Implementation
NSMutableDictionary *dictFields = [[NSMutableDictionary alloc] init];
NSMutableArray *arrFieldvalue = [[NSMutableArray alloc] init];
[dictFields setObject:[[self->dictCustomFieldGroups valueForKey:#"name"] objectAtIndex:iTag] forKey:#"fieldname"];
[dictFields setObject:[NSString stringWithFormat:#"%ld",(long)iTag] forKey:#"tagvalue"];
if ([self isTagIDAvailable:iTag]) {
arrFieldvalue = [[[self->arrTempCheckboxFields objectAtIndex:self->checkboxIndex] valueForKey:#"fieldvalue"] mutableCopy];
BOOL isTheObjectThere = [arrFieldvalue containsObject: strValue];
if (isTheObjectThere) {
NSLog(#"string contains");
[btn setBackgroundImage:[UIImage systemImageNamed:#"square"] forState:UIControlStateNormal];
for(id item in arrFieldvalue) {
if([item isEqual:strValue]) {
[arrFieldvalue removeObject:item];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
break;
}
}
} else {
NSLog(#"string does not contain!");
[btn setBackgroundImage:[UIImage systemImageNamed:#"checkmark.square"] forState:UIControlStateNormal];
[arrFieldvalue addObject:strValue];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
}
NSInteger count = [self->arrFields count];
for (NSInteger index = (count - 1); index >= 0; index--) {
NSArray *p = self->arrFields[index];
if ([[self->arrFields valueForKey:#"tagvalue"] isEqual:[NSNumber numberWithInt:iTag]]) {
[self->arrFields removeObjectAtIndex:index];
}
}
[self->arrFields replaceObjectAtIndex:iTag withObject:dictFields];
[self->arrTempCheckboxFields replaceObjectAtIndex:self->checkboxIndex withObject:dictFields];
} else {
[btn setBackgroundImage:[UIImage systemImageNamed:#"checkmark.square"] forState:UIControlStateNormal];
[arrFieldvalue addObject:strValue];
[dictFields setObject:arrFieldvalue forKey:#"fieldvalue"];
[self->arrFields addObject:dictFields];
[self->arrTempCheckboxFields addObject:dictFields];
}
NSLog(#"self->arrField: %#",self->arrFields);
});
}
-(BOOL) isTagIDAvailable:(int)tagID {
BOOL retVal = FALSE;
BOOL tagIdFound = FALSE;
if ([arrTagID count] > 0) {
for (int i = 0; i < [arrTagID count]; i++) {
if ([[NSNumber numberWithInteger:tagID] isEqualToNumber:[arrTagID objectAtIndex:i]]) {
tagIdFound = TRUE;
checkboxIndex = i;
retVal = TRUE;
break;
}
}
if (!tagIdFound) {
[arrTagID addObject:[NSNumber numberWithInteger:tagID]];
checkboxIndex = (int)[arrTagID count];
retVal = FALSE;
}
} else {
[arrTagID addObject:[NSNumber numberWithInteger:tagID]];
retVal = FALSE;
}
return retVal;
}
I have done something from line no 32, like I will remove that array value and add it again.But I am not sure how to do that. If more detail require I can provide.
Thanks in Advance.
I'm trying to create a converter which will make a video out of set of images. Everything is at its place, AVFormatContext, AVCodecContext, AVCodec. I'm creating YUV AVFrame out of UIImage and send it to encoder by avcodec_send_frame() method. Everything goes fine until I'm trying to get AVPacket with method avcodec_receive_packet(). Every time it returns -53 which means - output is not available in the current state - user must try to send input. As I said, I'm sending an input before I'm trying to get something and sending is successful.
Here's my code:
Init ffmpeg entities:
- (BOOL)setupForConvert:(DummyFVPVideoFile *)videoFile outputPath:(NSString *)path
{
if (!videoFile) {
[self.delegate convertationFailed:#"VideoFile is nil!"];
return NO;
}
currentVideoFile = videoFile;
outputPath = path;
BOOL success = NO;
success = [self initFormatCtxAndCodecs:path];
if (!success) {
return NO;
}
success = [self addCameraStreams:videoFile];
if (!success) {
return NO;
}
success = [self openIOContext:path];
if (!success) {
return NO;
}
return YES;
}
- (BOOL)initFormatCtxAndCodecs:(NSString *)path
{
//AVOutputFormat *fmt = av_guess_format("mp4", NULL, NULL);
int ret = avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, [path UTF8String]);
if (ret < 0) {
NSLog(#"Couldn't create output context");
return NO;
}
//encoder codec init
pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!pCodec) {
NSLog(#"Couldn't find a encoder codec!");
return NO;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
NSLog(#"Couldn't alloc encoder codec context!");
return NO;
}
pCodecCtx->codec_tag = AV_CODEC_ID_H264;
pCodecCtx->bit_rate = 400000;
pCodecCtx->width = currentVideoFile.size.width;
pCodecCtx->height = currentVideoFile.size.height;
pCodecCtx->time_base = (AVRational){1, (int)currentVideoFile.framerate};
pCodecCtx->framerate = (AVRational){(int)currentVideoFile.framerate, 1};
pCodecCtx->gop_size = 10;
pCodecCtx->max_b_frames = 1;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
NSLog(#"Couldn't open the encoder codec!");
return NO;
}
pPacket = av_packet_alloc();
return YES;
}
- (BOOL)addCameraStreams:(DummyFVPVideoFile *)videoFile
{
AVCodecParameters *params = avcodec_parameters_alloc();
if (!params) {
NSLog(#"Couldn't allocate codec parameters!");
return NO;
}
if (avcodec_parameters_from_context(params, pCodecCtx) < 0) {
NSLog(#"Couldn't copy parameters from context!");
return NO;
}
for (int i = 0; i < videoFile.idCameras.count - 1; i++)
{
NSString *path = [videoFile.url URLByAppendingPathComponent:videoFile.idCameras[i]].path;
AVStream *stream = avformat_new_stream(pFormatCtx, pCodec);
if (!stream) {
NSLog(#"Couldn't alloc stream!");
return NO;
}
if (avcodec_parameters_copy(stream->codecpar, params) < 0) {
NSLog(#"Couldn't copy parameters into stream!");
return NO;
}
stream->avg_frame_rate.num = videoFile.framerate;
stream->avg_frame_rate.den = 1;
stream->codecpar->codec_tag = 0; //some silly workaround
stream->index = i;
streams[path] = [[VideoStream alloc] initWithStream:stream];
}
return YES;
}
- (BOOL)openIOContext:(NSString *)path
{
AVIOContext *ioCtx = nil;
if (avio_open(&ioCtx, [path UTF8String], AVIO_FLAG_WRITE) < 0) {
return NO;
}
pFormatCtx->pb = ioCtx;
return YES;
}
And here's convertation process:
- (void)launchConvert:(DummyFVPVideoFile *)videoFile
{
BOOL convertInProgress = YES;
unsigned int frameCount = 1;
unsigned long pts = 0;
BOOL success = NO;
success = [self writeHeader];
if (!success) {
NSLog(#"Couldn't write header!");
return;
}
AVRational defaultTimeBase;
defaultTimeBase.num = 1;
defaultTimeBase.den = videoFile.framerate;
AVRational streamTimeBase = streams.allValues.firstObject.stream->time_base;
while (convertInProgress)
{
pts += av_rescale_q(1, defaultTimeBase, streamTimeBase);
for (NSString *path in streams.allKeys)
{
UIImage *img = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/%u.jpg", path, frameCount]];
AVPacket *pkt = [self getAVPacket:img withPts:pts];
if (!pkt->data) { continue; }
pkt->stream_index = streams[path].stream->index;
//check all settings of pkt
if (![self writePacket:pkt]) {
NSLog(#"Couldn't write packet!");
convertInProgress = NO;
break;
}
}
frameCount++;
}
success = [self writeTrailer];
if (!success) {
NSLog(#"Couldn't write trailer!");
return;
}
NSLog(#"Convertation finished!");
//delegate convertationFinished method
}
- (BOOL)writeHeader
{
if (avformat_write_header(pFormatCtx, NULL) < 0) {
return NO;
}
return YES;
}
- (BOOL)writePacket:(AVPacket *)pkt
{
if (av_interleaved_write_frame(pFormatCtx, pkt) != 0) {
return NO;
}
return YES;
}
- (BOOL)writeTrailer
{
if (av_write_trailer(pFormatCtx) != 0) {
return NO;
}
return YES;
}
/**
This method will create AVPacket out of UIImage.
#return AVPacket
*/
- (AVPacket *)getAVPacket:(UIImage *)img withPts:(unsigned long)pts
{
if (!img) {
NSLog(#"imgData is nil!");
return nil;
}
uint8_t *imgData = [self getPixelDataFromImage:img];
AVFrame *frame_yuv = av_frame_alloc();
if (!frame_yuv) {
NSLog(#"frame_yuv is nil!");
return nil;
}
frame_yuv->format = AV_PIX_FMT_YUV420P;
frame_yuv->width = (int)img.size.width;
frame_yuv->height = (int)img.size.height;
int ret = av_image_alloc(frame_yuv->data,
frame_yuv->linesize,
frame_yuv->width,
frame_yuv->height,
frame_yuv->format,
32);
if (ret < 0) {
NSLog(#"Couldn't alloc yuv frame!");
return nil;
}
struct SwsContext *sws_ctx = nil;
sws_ctx = sws_getContext((int)img.size.width, (int)img.size.height, AV_PIX_FMT_RGB24,
(int)img.size.width, (int)img.size.height, AV_PIX_FMT_YUV420P,
0, NULL, NULL, NULL);
const uint8_t *scaleData[1] = { imgData };
int inLineSize[1] = { 4 * img.size.width };
sws_scale(sws_ctx, scaleData, inLineSize, 0, (int)img.size.height, frame_yuv->data, frame_yuv->linesize);
frame_yuv->pict_type = AV_PICTURE_TYPE_I;
frame_yuv->pts = pCodecCtx->frame_number;
ret = avcodec_send_frame(pCodecCtx, frame_yuv); //every time everything is fine
if (ret != 0) {
NSLog(#"Couldn't send yuv frame!");
return nil;
}
av_init_packet(pPacket);
pPacket->dts = pPacket->pts = pts;
do {
ret = avcodec_receive_packet(pCodecCtx, pPacket); //every time -35 error
NSLog(#"ret = %d", ret);
if (ret == AVERROR_EOF) {
NSLog(#"AVERROR_EOF!");
} else if (ret == AVERROR(EAGAIN)) {
NSLog(#"AVERROR(EAGAIN)");
} else if (ret == AVERROR(EINVAL)) {
NSLog(#"AVERROR(EINVAL)");
}
if (ret != 0) {
NSLog(#"Couldn't receive packet!");
//return nil;
}
} while ( ret == 0 );
free(imgData);
av_packet_unref(pPacket);
av_packet_free(pPacket);
av_frame_unref(&frame_yuv);
av_frame_free(&frame_yuv);
//perform other clean up and test dat shit
return pPacket;
}
Any insights would be helpful. Thanks!
There may be two reasons.
According to on of the documents of FFmpeg you may need to feed more then one packet to avcodec_send_frame() to receive packet return successful.
I cannot confirm that you allocated enough sized buffer for pPacket. The functions av_packet_alloc() and av_init_packet() won't allocate any buffer but latter sets it to NULL instead. So allocation must be done after init. Somewhere you should allocate buffer either manually or with av_new_packet(pPacket, SIZE).
Hope that helps.
I'm trying to create 10 balloons in my app. I create a random CGPoint to set my node's position and check, in case that already exist a node at this point I try again.
I don't know why, the balloons keep being placed over another balloon.
Any ideia of what I am doing wrong?
Here is my code:
-(void)gerarBaloes:(int)quantidade
{
balloons = [NSMutableArray new];
u_int32_t maxHorizontal = 900;
u_int32_t minHorizontal = 100;
u_int32_t maxVertical = 650;
u_int32_t minVertical = 100;
for (int i=1; i<= quantidade; i++) {
CGPoint posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
while (![self validPosition:posicao]) {
posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
}
balao* nBalao = [[balao alloc]initWithPosition:posicao];
SKLabelNode* numero = [[SKLabelNode alloc]initWithFontNamed:#"Arial Rounded MT Bold"];
numero.text = [NSString stringWithFormat:#"%d",i];
numero.zPosition = 1;
nBalao.body.zPosition = 0;
[nBalao addChild:numero];
nBalao.body.name = #"balloon";
[balloons addObject:nBalao];
[self addChild:nBalao];
}
}
And this is my validation method:
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self nodesAtPoint:position];
NSLog(#"total de nodes %d",nodes.count);
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
return NO;
}
}
return YES;
}else{
return YES;
}
}
Balao.m class:
#implementation balao
-(id)initWithPosition:(CGPoint)pos
{
if (self = [super init]) {
self.position = pos;
int n = arc4random_uniform(4);
_balloonAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:#"balloon%d",n]];
_explosionFrames = [NSMutableArray array];
int numImages = _balloonAtlas.textureNames.count;
for (int i=1; i<= numImages; i++){
NSString *textureName = [NSString stringWithFormat:#"balloon_%d",i];
SKTexture *temp = [_balloonAtlas textureNamed:textureName];
[_explosionFrames addObject:temp];
}
SKTexture *textInicial = [_explosionFrames[0] copy];
[_explosionFrames removeObjectAtIndex:0];
self.body = [SKSpriteNode spriteNodeWithTexture:textInicial];
[self addChild:_body];
}
return self;
}
- (void)explodeBalloon
{
SKAction* explosao = [SKAction animateWithTextures:_explosionFrames timePerFrame:0.02f resize:NO restore:YES];
[self.body runAction:explosao completion:^(void){
[self removeFromParent];
}];
[self runAction:[SKAction playSoundFileNamed:#"popSound.mp3" waitForCompletion:NO]];
}
This is how the balloons appears:
Edit:
I tried the suggested method, with CGRectCointainsPoint, but it didn't work too. :/
Checking for nodes at a given point is not enough to prevent overlap. You need to check whether the point falls inside the frame of another balloon. You can modify the function like this
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self children];
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
if (CGRectContainsPoint([n getBalloonFrame], position)) // Changed line
{
return NO
}
}
}
return YES;
}
return YES;
}
CGRectContainsPoint checks whether a given rectangle contains a point.
Inside balao class define the following function. Also note the changed line in the above code.
-(void)getBalloonFrame
{
return CGRectOffset(self.body.frame, self.frame.origin.x, self.frame.origin.y)
}
I hava a Play audio class used AudioToolBox.framework ,AudioQueue.
I encountered a problem, every piece of audio data playback, the memory will be increased, after playback is complete, the memory will not be reduced. If the batch test, it will be added to the hundreds of megabytes of memory, I want to know what causes memory has been increased, the audio data on each pass of each object is released or other reasons.
Here is my playThread class code:
#interface PlayThread()
{
BOOL transferDataComplete; // if thers is no data transfer to playthread set transferDataComplete = yes;
NSMutableArray *receiveDataArray;// audio data array
BOOL isPlay;// if audioqueue start,isPlay = yes,
}
#end
#pragma mark class implementation
#implementation PlayThread
- (instancetype)init
{
if (self = [super init]) {
receiveDataArray = [[NSMutableArray alloc]init];
isPlay = NO;
transferDataComplete = false;
bufferOverCount = QUEUE_BUFFER_SIZE;
audioQueue = nil;
}
return self;
}
// audio queue callback function
static void BufferCallback(void *inUserData,AudioQueueRef inAQ,AudioQueueBufferRef buffer)
{
USCPlayThread* player=(__bridge USCPlayThread*)inUserData;
[player fillBuffer:inAQ queueBuffer:buffer];
}
// fill buffer
-(void)fillBuffer:(AudioQueueRef)queue queueBuffer:(AudioQueueBufferRef)buffer
{
while (true){
NSData *audioData = [self getAudioData];
if( transferDataComplete && audioData == nil) {
bufferOverCount --;
break;
}
else if(audioData != nil){
memcpy(buffer->mAudioData, [audioData bytes] , audioData.length);
buffer->mAudioDataByteSize = (UInt32)audioData.length;
AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
break;
}
else
break;
} // while
if(bufferOverCount == 0){
// stop audioqueue
[self stopAudioQueue];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:#selector(playComplete)]) {
[self.delegate playComplete];
}
});
}
}
-(void)addPlayData:(NSData *)data
{
NSUInteger count = 0;
#synchronized(receiveDataArray){
[receiveDataArray addObject:data];
}
}
/**
* get data from receiveDataArray
*/
-(NSData*)getAudioData
{
NSData *headData = nil;
#synchronized(receiveDataArray){
if(receiveDataArray.count > 0){
headData = [receiveDataArray objectAtIndex:0];
[receiveDataArray removeObjectAtIndex:0];
}
}
return headData;
}
- (void)startPlay // start audioqueue to play audio data
{
[self reset];
[self open];
for(int i=0; i<QUEUE_BUFFER_SIZE; i++)
{
[self fillBuffer:audioQueue queueBuffer:audioQueueBuffers[i]];
}
// audioqueuestart
AudioQueueStart(audioQueue, NULL);
#synchronized(self){
isPlay = YES;
}
if ([self.delegate respondsToSelector:#selector(playBegin)]) {
[self.delegate playBegin];
}
}
-(void)createAudioQueue
{
if (audioQueue) {
return;
}
AudioQueueNewOutput(&audioDescription, BufferCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);
if(audioQueue){
for(int i=0;i<QUEUE_BUFFER_SIZE;i++){
AudioQueueAllocateBufferWithPacketDescriptions(audioQueue, EVERY_READ_LENGTH, 0, &audioQueueBuffers[i]);
}
}
}
-(void)stopAudioQueue
{
if(audioQueue == nil){
return;
}
#synchronized(self){
if(isPlay){
isPlay = NO;
}
}
AudioQueueStop(audioQueue, TRUE);
}
-(void)setAudioFormat
{
audioDescription.mSampleRate = 16000;
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioDescription.mChannelsPerFrame = 1;
audioDescription.mFramesPerPacket = 1;
audioDescription.mBitsPerChannel = 16;
audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
}
-(void)close
{
if (audioQueue) {
AudioQueueStop(audioQueue, true);
AudioQueueDispose(audioQueue, true);
audioQueue = nil;
isPlay = NO;
}
}
-(BOOL)open {
if([self isOpen]){
return YES;
}
[self close];
[self setAudioFormat];
[self createAudioQueue];
return YES;
}
-(BOOL)isOpen
{
return (audioQueue != nil);
}
- (void)reset
{
bufferOverCount = QUEUE_BUFFER_SIZE;
transferDataComplete = NO;
}
- (BOOL)isPlaying
{
return isPlay;
}
- (void)disposeQueue
{
if (audioQueue) {
AudioQueueDispose(audioQueue, YES);
}
audioQueue = nil;
}
- (void)dealloc
{
[self disposeQueue];
}
Here is ViewContrller.m :
- (void)viewDidLoad {
[super viewDidLoad];
PlayThread *playThread = [[PlayThread alloc]init];
playThread.delegate = self;
self.playThread = playThread;
for (int i = 0; i < 10; i++)
{ // create empth audio data to simulate
NSMutableData *data = [[NSMutableData alloc]initWithLength:10000];
[self.playThread addPlayData:data];
}
[self.playThread startPlay];
}
Here is PlayThread's delegate method:
// When the play completely,then play once again,memory will continue to increase
- (void)playComplete
{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 10; i++)
{
NSMutableData *data = [[NSMutableData alloc]initWithLength:10000];
[self.playThread addPlayData:data];
}
[self.playThread startPlay];
});
}
Why memory has continued to increase, how can promptly release memory?
AudioQueueNewOutput(&audioDescription, BufferCallback, (__bridge void *)(self), nil, nil, 0, &audioQueue);
here parameter can not be nil
I have a UITabBarController with 3 view controllers connected to it. Everything works fine apart from when you switch from one view to another, the new view takes a moment to reload what it was doing. I know this isn't such a big deal but it just makes the app look a bit sloppy. Does anyone know how I can keep the app running in the background, or something so that it doesn't have to reload each time.
As you might have noticed I'm very new to Objective-C so I can understand if I'm being unclear but any help is really appreciated!
EDIT: FOR DAVID
This is the code for the stopwatch in the .m file:
#implementation StopwatchViewController
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
isCounting = false;
}
- (IBAction)startOrStop:(id)sender
{
if (self->isCounting == false) {
self->isCounting = true;
[self startStopwatch];
} else {
self->isCounting = false;
[self stopStopwatch];
}
}
- (void)startStopwatch
{
[startStopButton setTitle:#"STOP" forState:UIControlStateNormal];
[self performSelector:#selector(stopwatch) withObject:self afterDelay:1.0];
}
- (IBAction)resetStopwatch:(id)sender
{
[self reset];
}
- (void)stopwatch
{
if (self->isCounting == false) {
return;
} else {
NSInteger hourInt = [hourLabel.text intValue];
NSInteger minuteInt = [minuteLabel.text intValue];
NSInteger secondInt = [secondLabel.text intValue];
if (secondInt == 59) {
secondInt = 0;
if (minuteInt == 59) {
minuteInt = 0;
if (hourInt == 23) {
hourInt = 0;
} else {
hourInt += 1;
}
} else {
minuteInt += 1;
}
} else {
secondInt += 1;
}
NSString *hourString = [NSString stringWithFormat:#"%02d", hourInt];
NSString *minuteString = [NSString stringWithFormat:#"%02d", minuteInt];
NSString *secondString = [NSString stringWithFormat:#"%02d", secondInt];
hourLabel.text = hourString;
minuteLabel.text = minuteString;
secondLabel.text = secondString;
CGRect hourFrame = self->hourBar.frame;
CGRect minuteFrame = self->minuteBar.frame;
CGRect secondFrame = self->secondBar.frame;
if ((NSInteger)hourFrame.size.height != hourInt) { // check if we need to modify
hourFrame.origin.y -= ((hourInt * 10.0) - hourFrame.size.height);
hourFrame.size.height = (hourInt * 10.0);
self->hourBar.frame = hourFrame;
}
if ((NSInteger)minuteFrame.size.height != minuteInt) { // check if we need to modify
minuteFrame.origin.y -= ((minuteInt * 4.0) - minuteFrame.size.height);
minuteFrame.size.height = (minuteInt * 4.0);
self->minuteBar.frame = minuteFrame;
}
if ((NSInteger)secondFrame.size.height != secondInt) { // check if we need to modify
secondFrame.origin.y -= ((secondInt * 4.0) - secondFrame.size.height);
secondFrame.size.height = (secondInt * 4.0);
self->secondBar.frame = secondFrame;
}
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(stopwatch) userInfo:nil repeats:NO];
}
}
- (void)stopStopwatch
{
[startStopButton setTitle:#"START" forState:UIControlStateNormal];
}
- (void)reset
{
[startStopButton setTitle:#"START" forState:UIControlStateNormal];
self->isCounting = false;
hourLabel.text = #"00";
minuteLabel.text = #"00";
secondLabel.text = #"00";
CGRect hourFrame = self->hourBar.frame;
CGRect minuteFrame = self->minuteBar.frame;
CGRect secondFrame = self->secondBar.frame;
hourFrame.size.height = 0;
minuteFrame.size.height = 0;
secondFrame.size.height = 0;
hourFrame.origin.y = 321.0;
minuteFrame.origin.y = 321.0;
secondFrame.origin.y = 321.0;
self->hourBar.frame = hourFrame;
self->minuteBar.frame = minuteFrame;
self->secondBar.frame = secondFrame;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
SECOND EDIT FOR DAVID:
Changed the main parts of the code to look like this:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:YES];
[self swapFrames];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[self updateBars];
}
- (void)stopwatch
{
if (self->isCounting == false) {
return;
} else {
hourInt = [hourLabel.text intValue];
minuteInt = [minuteLabel.text intValue];
secondInt = [secondLabel.text intValue];
if (secondInt == 59) {
secondInt = 0;
if (minuteInt == 59) {
minuteInt = 0;
if (hourInt == 23) {
hourInt = 0;
} else {
hourInt += 1;
}
} else {
minuteInt += 1;
}
} else {
secondInt += 1;
}
NSString *hourString = [NSString stringWithFormat:#"%02d", hourInt];
NSString *minuteString = [NSString stringWithFormat:#"%02d", minuteInt];
NSString *secondString = [NSString stringWithFormat:#"%02d", secondInt];
hourLabel.text = hourString;
minuteLabel.text = minuteString;
secondLabel.text = secondString;
[self swapFrames];
[self updateBars];
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(stopwatch) userInfo:nil repeats:NO];
}
}
- (void)updateBars
{
if ((NSInteger)hourFrame.size.height != hourInt) { // check if we need to modify
hourFrame.origin.y -= ((hourInt * 10.0) - hourFrame.size.height);
hourFrame.size.height = (hourInt * 10.0);
self->hourBar.frame = hourFrame;
}
if ((NSInteger)minuteFrame.size.height != minuteInt) { // check if we need to modify
minuteFrame.origin.y -= ((minuteInt * 4.0) - minuteFrame.size.height);
minuteFrame.size.height = (minuteInt * 4.0);
self->minuteBar.frame = minuteFrame;
}
if ((NSInteger)secondFrame.size.height != (secondInt * 4.0)) { // check if we need to modify
secondFrame.origin.y -= ((secondInt * 4.0) - secondFrame.size.height);
secondFrame.size.height = (secondInt * 4.0);
self->secondBar.frame = secondFrame;
}
}
- (void)swapFrames
{
hourFrame = self->hourBar.frame;
minuteFrame = self->minuteBar.frame;
secondFrame = self->secondBar.frame;
}
I separated the code so that just before the view appear it should update the bars. However, it did not work. I did some investigating by printing out the values of some of the variables at certain points. It appears that in viewWillAppear, secondBar.frame (and minuteBar, etc.) has updated to the correct height. However, in viewDidAppear, that value is reset to 0. This does not happen to secondInt, or secondFrame. Somewhere between those two methods the frame is reset but I cannot figure it out.
From what I understand, the frames for your properties self.hourBar, self.minuteBar and self.secondBar are set to 0 when you switch between tabs, and only update them every second.
If this is indeed the case, just set them to their correct values in your viewControllers viewWillAppear: method (assign them to some property in viewWillDisappear:).
As a sidenote, you seem to be coming from C++. The "->" notation is very uncommon for Objective-C, since properties are accessed with ".", and their corresponding instance variable with "->". Using the arrow notation will not call the auto-synthesised getter/setter methods of properties!
Also, is there a specific reason why you always create new NSTimer objects instead of setting repeats: to yes? Creating a timer and adding it to a runloop (which scheduledTimerWith:... does) is a relatively costly operation.