We're using version 1.2 (and trying beta 1.3.1) of the ZBarSDK for reading barcodes in our app. We have been using this code for some time with no problems in iOS 5 and 6, but iOS 7 appears to have some issues initializing the camera. When we call the start method in the ZBarReaderView it takes 30 to 60 seconds or even longer to initialize the camera. The user sees a black camera view that whole time and they are wondering if the app works.
Once the view is initialized we can close it and reopen it any number of times with no issues and it opens rapidly.
Here is the relevant part of our code:
- (void)showAnimated:(BOOL)animated
{
[self.controller overlayWillShow:self];
dispatch_async(dispatch_queue_create(0, 0), ^{[self.zBarReaderView start];});
CGRect frame = self.superview.frame;
frame.origin.y = -hiddenYOrigin;
[UIView animateWithDuration:0.4 animations:^{
self.superview.frame = frame;
} completion:^(BOOL finished){
if (((ScanController *)self.controller).scanMode == ScanModeManualEntry) {
[self.manualEntryTextField becomeFirstResponder];
}
self.showing = YES;
[self.controller overlayDidShow:self];
}];
}
All of that code executes rapidly, but the dispatch_queue_create call to ZBarReaderView start is where we are hung up for a long time.
Any ideas?
Your problem might be related to this (check the comments):
iOS7 : UIImageView Takes Forever to Appear
We don't know which commands [self.zBarReaderView start] actually executes, but the documentation for ZBarReaderView says :
This is a barcode reader encapsulted in a UIView.
If it's laying out subview in UIViews, supposedly it's not safe to call "start" in a background thread, and while in iOS6 this worked it doesn't in iOS7 anymore, since the internals have changed.
Try to init the camera in the main thread, and see if it works:
dispatch_async(dispatch_get_main_queue(), ^{[self.zBarReaderView start];});
Related
I have a situation that is confusing me lots. I have a class that has 2 completely separate things: An animated UIImage and a UILabel. They have different outlets, are not connected.
When the app runs, it does this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.monsterMachine setHidden:NO]; //monsterMachine is UIImageView
[self.monsterMachine startAnimating];
});
but then when I do this:
[self.futText setText:#"blah"]; // is UILabel
it causes the monsterMachine UIImageView to not animate anymore. Things I have found:
Right after I setText:#blah" I can use NSLog to watch and see that self.monsterMachine.isAnimated suddenly goes from 1 to 0, ie from YES to NO.
If self.futText is ALREADY saying "blah", then I can run setText:#"blah" on it as many times as I want and nothing happens, it is ONLY when I change it to some value other than blah that UIImageView stops animating and disappears
If I don't use main_queue to show and animate monsterMachine, it won't display at ALL, so how do I diagnose or fix this?
That's weird it works for me perfect. Did you call [self.futText setText:#"blah"]; in main queue also?
Here is a small answer to why setting text #"blah" won't stops the animation:
Strings like #"blah" are stored in stacks which have the same reference as long as they have the same context. In this case [self.futText setText:#"blah"] will do nothing because the internal implementions are like:
void setText:(NSString*) text {
if ( _text == text ) then return;//if reference is the same then do nothing
_text = text;
some rendering..
}
This question already has answers here:
Calling sleep(5); and updating text field not working
(4 answers)
Closed 8 years ago.
In my Application, I have a NSMutableArray with UIImage in it.
I would like to display the first UIImage in the array for three seconds, and then to
display the second image.
All of this should happen when I press a UIButton.
Below is my code:
[testImageView setImage:[arr objectAtIndex:0]] ;
sleep(3) ;
[testImageView setImage:[arr objectAtIndex:1]] ;
testImageView is a UIImageView object on my screen.
When I'm running this code, my button remain pressed for three seconds and only the second image is displayed.
What should I do?
Try using one of the most obscure methods of UIImageView.
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:...];
myImageView.animationImages = images;
myImageView.animationDuration = 3.0 * images.count;
[self.view addSubview:myImageView];
Then, when you wanna start animating
[myImageView startAnimating];
I don't know what you plan on doing with this, but 3 seconds might be too much. If you're doing some sort of presentation-ish, then this method might not be very good, since there's no easy way of going back.
The reason that your button remains pressed is because you slept main thread for 3 seconds, this means that nothing is going to happen to your application (at least the user interface) until thread is back to active.
There are multiple ways to achieve what you wish, but you must put a timer on the background thread. I would suggest you put the code to set the second image in another method first and make array into a property.
- (void)setImage
{
[testImageView setImage:self.yourArray[1]];
}
Then you can use one of the following ways to execute the method:
Use a NSTimer:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(setImage) userInfo:nil repeats:NO]
Use performSelector method of NSObject:
[self performSelector:#selector(setImage) withObject:nil afterDelay:3.0];
Use Grand Central Dispatch.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3.0), dispatch_get_main_queue(), ^{
[self setImage];
});
Any of the ways described above will work.
Read more information about concurrency on the links below:
http://jeffreysambells.com/2013/03/01/asynchronous-operations-in-ios-with-grand-central-dispatchNS
https://developer.apple.com/library/ios/documentation/general/conceptual/concurrencyprogrammingguide/Introduction/Introduction.html
how to call a method of multiple arguments with delay
How can I delay a method call for 1 second?
I'm developing a ios app. I use this code on a view, in order to made an activity indicator and a black background appear in place of the whole view while is loading.
// hiding all the uioutlet in the view
sfondo.hidden=TRUE;
dalText.hidden=TRUE;
alText.hidden=TRUE;
titoloText.hidden=TRUE;
// generating the activity indicator
CGRect screenBound = [[UIScreen mainScreen] bounds];
CGSize screenSize = screenBound.size;
CGFloat screenWidth = screenSize.width;
CGFloat screenHeight = screenSize.height;
imgCaricamento = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[imgCaricamento setCenter:CGPointMake(screenWidth/2.0, screenHeight/2.0)]; // I do this because I'm in landscape mode
[self.view addSubview:imgCaricamento]; // spinner is not visible until started
[imgCaricamento startAnimating]; // start!
Now the problem is that i launch this code before starting an update and do several things. What happen is that, at the moment this code is launched the view freezes for one-two seconds, only after this delay time, the activity appears. This time it's enough to convey to the user the impression that the app is acting bad... How can i make this code being executed before and immediately?
You are probably doing something that is blocking the main thread, maybe downloading an image, reading something from disk or parsing some data. The main thread is responsible for refreshing the UI and when it is blocked, the UI freezes. You can solve this by doing your processing in the background, on a different thread.
Apart from something maybe blocking the main thread therefore preventing the UI being updated I would prepare the UIActivityIndicatorView within viewWillAppear and execute [imgCaricamento startAnimating]; within viewDidAppear or where ever you start the updating process.
I've a strange problem with sliding in a UIView into another one while sliding out the other content of the view.
This is the code:
- (void)moveAddStudentViewIntoSuperView
{
CGRect viewFrame1 = self.viewThatShoudlDisappear1.frame;
CGRect viewFrame2 = self.viewThatShoudlDisappear2.frame;
CGRect newFrame = self.viewControllerThatshouldAppear.view.frame;
viewFrame1.origin.x = -300;
viewFrame2.origin.x = -300;
newStudentFrame.origin.x = 0;
NSLog(#"\n 1: %# \n 2: %# \n 3: %#", self.viewThatShoudlDisappear1, self.viewThatShoudlDisappear2, self.viewControllerThatshouldAppear.view);
[self.viewThatShoudlDisappear1 setFrame:viewFrame1];
[self.viewThatShoudlDisappear2 setFrame:viewFrame2];
[self.viewControllerThatshouldAppear.view setFrame:newFrame];
NSLog(#"\n 1: %# \n 2: %# \n 3: %#", self.viewThatShoudlDisappear1, self.viewThatShoudlDisappear2, self.viewControllerThatshouldAppear.view);
}
(Usually I'd do the setFrames in a UIView animation but it doesn't matter for this example as it doesn't work anyway)
What happens is that the view that should slide in or rather is now located at (0,0) appears but the other views dont move away. And are still displayed under the new view.
What is strange, is that the log outputs that all views are in the correct position. [self.view setNeedsLayout] between the setFrame calls doesn't change anything either and even more interesting is that if I remove the setFrame from the new view the views that should disappear do actually disappear correctly.
There is a typo on this line:
CGRect viewFrame2 = self.viewThatShoudlDisappear1.frame;
Presumably, this should be:
CGRect viewFrame2 = self.viewThatShoudlDisappear2.frame;
I've seen odd things like this when I've accidentally used UIKit from a background thread. UIKit should only be used from the main thread.
Ok so I think I don't fully understand AutoLayout. Due to a tip from Toto, I tried to test it under iOS 5 and had to deactivate AutoLayout everywhere to run it.
It instantly worked with iOS 5. Afterwards I switched back to iOS 6 without activating AutoLayout again and it worked there perfectly too.
My solution is: Deactivating AutoLayout in the XIB file(s).
But I can't think of a way where this should be a default behavior. Maybe I miss a point of AutoLayout or this is a weird bug. As soon as I've talked to devs who are more into it, I'll probably submit a bugreport if necessary.
The iPad app I'm working on is a book. To jump to a specific page, the user can press a button that overlays a view top of the current view, displaying images of thumbnails of each page in the book.
When the user goes through the book sequentially and displays this thumbnails menu, the scrolling animation is smooth and fine if the user showed the menu . The problem happens if the user calls showBookmarkMenu after having loaded about fifteen pages, the scrollview animation is very very slow, and the scrollview doesn't catch touches anymore.
I noticed that scrollViewDidEndDecelerating gets called when the scrolling animation is normal and smooth (shortly after loading the app), but it doesn't get called after the user has gone through several pages. So one hypothesis is that the CPU is struggling with the animation of the positioning of the scrollview's content. I ran the app using Instruments' Activity Monitor, but there are times when the app uses 97% and more of the CPU and the scrollview scrolls fine...
Any thoughts on this issue? I've posted my code below.
MainClass.m
//Called when user presses the open/close bookmark menu button
-(IBAction)toggleBookmarksMenu{
if([bookMarkMenu isHidden]){
[currentPage.view addSubview:bookMarkMenu];
[bookMarkMenu showBookmarkMenu];
}
else{
[bookMarkMenu hideBookmarksMenu];
}
}
ScrollViewClass.h
#interface BookmarkManager : UIView<UIScrollViewDelegate>{
UIScrollView *thumbnailScrollView;
}
#property (strong, nonatomic) UIScrollView *thumbnailScrollView;
#property (strong) id <BookmarkManagerDelegate> bookmarkManagerDelegate;
-(void)showBookmarkMenu;
-(void)hideBookmarksMenu;
#end
ScrollViewClass.m
-(void)showBookmarkMenu{
[self setHidden:NO];
[UIView animateWithDuration:0.5
animations:^{
self.center = CGPointMake(512, 384);
}
];
}
-(void)hideBookmarksMenu{
[UIView animateWithDuration:1
animations:^{
self.center = CGPointMake(512, -120);
}
completion:^(BOOL finished){
[self setHidden:YES];
[self removeFromSuperview];
}
];
}
-(id)init{
self = [super initWithFrame:CGRectMake(0, 0, 1024, 768)];
if(self){
[self setBackgroundColor:[UIColor clearColor]];
self.center = CGPointMake(512, 0);
thumbnailScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 120)];
[thumbnailScrollView setBackgroundColor:[UIColor clearColor]];
thumbnailScrollView.showsHorizontalScrollIndicator = NO;
//Add the UIButtons with images of the thumbnails
for(int i = 0; i < totalPages; i++){
UIButton *pageThumbnail = [UIButton buttonWithType:UIButtonTypeCustom];
pageThumbnail.frame = CGRectMake(0, 0, 125, 95);
[pageThumbnail setBackgroundImage:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/p%d_thumb.png", [[NSBundle mainBundle] resourcePath], i]] forState:UIControlStateNormal];
[thumbnailScrollView addSubview:pageThumbnail];
[pageThumbnail addTarget:self action:#selector(thumbnailTapped:) forControlEvents:UIControlEventTouchDown];
}
[self addSubview:thumbnailScrollView];
[thumbnailScrollView setContentSize:CGSizeMake(totalPages * 125 + (20*(totalPages+1)), 120)];
[thumbnailScrollView setDelegate:self];
[self setHidden:YES];
}
return self;
}
I have to go with possible low memory issue.
A possible alternative to using a slew of buttons is using UITableView. The way your code is currently working, it loads up ALL the buttons with images. For a large book this could be painful.
Using UITableView you only use as much memory as you see (about). And, since each image is loaded dynamically, your memory usage is only as much as is displayed. That would be how I would go about it (actually, I'm doing that now, just not with a book).
A shot in the dark, based on your observation that the scrolling becomes slow after loading 15 pages or so: possibly your device is busy handling a low memory condition. In such cases, as you possibly know, a system wide notification is sent to a considerable number of apps/objects for them to recover as much memory as possible.
Could you check if at more or less the same time when the scrolling becomes slow your app is executing didReceiveMemoryWarning?
If you confirm that the issue could be related to memory saturation/reclaiming, then I would suggest implementing a lazy loading scheme for your images:
you only load images when you are required to display them;
you only keep in memory 3-5 images total, to ensure a smooth scrolling.
The basic step requires id providing your delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
implementation. Here you will preload images:
knowing your position, you know your current image (say, image number N);
unload images N-2, N+2;
load images N-1, N+1.
The images to load/unload I provided are fine if you just want one "buffer" image.
In any case, if you google "iso scroll view lazy loading" you will find plenty of info.
Turns out it wasn't a low memory issue, but an overly busy CPU issue.
It is the CPU that does the calculations required for the scrollview's scrolling animations, and when the scrolling becomes this slow I thought I'd try to figure out why I was using 97% of the CPU in the first place. Turns out that past page 15, I had CPU-intensive recursive functions (calculating UIBezierPaths for another part of the app) caught in an infinite loop. The app was calculating hundreds of UIBezierPaths a second, and there reached a point where the CPU just couldn't keep up with the calculations for the scrollview's animation.
Once I made sure the recursive functions stopped calling themselves when they were not needed, CPU usage remained under 20% throughout the app, and the scrollview performed perfectly well.