I'm cropping the scanning region of Zbar via the following code:
- (void)startScanning
{
NSLog(#"Scanning..");
reader = [AACZBarViewController new];
reader.readerDelegate=self;
reader.supportedOrientationsMask = ZBarOrientationMask(UIInterfaceOrientationPortrait);
reader.showsZBarControls = NO;
CGFloat x,y,w,h;
x =0;
y =0.25;
w=1;
h=0.50;
reader.scanCrop = CGRectMake(x,y,w,h); //Crop scan region
reader.cameraOverlayView = [self myOverlay];
ZBarImageScanner *scanner = reader.scanner;
[scanner setSymbology: ZBAR_I25 config: ZBAR_CFG_ENABLE to: 0];
[self presentViewController:reader animated:YES completion:nil];
}
The problem however is that the program still uses the entire screen area to find a barcode - not the middle 50%. I don't think the issue is the reader.scanCrop method, but as to what the real culprit is, I can't fathom.
Edit:
Anyone?
I had a look at the zbar documentation again and noticed it said the x axis on the camera is verticle - not horizontal. Now I'd set the reader to be portrait only but apparently this does not affect the camera in any way. I didn't find a way to change this, but I did manage to crop to the scanning region I wanted.
The solution:
If you want the following scan region (x,y,w,h) then you set the rectangle by swapping the x and y and width and height so do this (y,x,h,w). It doesn't seem to crop to the bounding box exactly, but it's close enough for my purposes.
Related
I'm currently creating a custom UISlider in Xcode. I am setting the thumb Image, min Image, and max Image for the slider. Everything works great. The design calls for the thumb image to be transparent to the background of the view. This is where my problem comes in, you can see the two sides of the slider coming together in the middle of the thumb image. I don't want it to look like this. I want the slider bars to stop a the edge of the thumb image. Any ideas as to how I can accomplish this? Any and all suggestions are welcome. Thanks.
This is the Code I'm using so far.
UIImage *thumbImage = [UIImage imageNamed:#"thumb.png"];
UIImage *minImage = [UIImage imageNamed:#"min.png"];
UIImage *maxImage = [UIImage imageNamed:#"max.png"];
minImage = [minImage resizableImageWithCapInsets:UIEdgeInsetsMake(0,0,0,0)];
maxImage = [maxImage resizableImageWithCapInsets:UIEdgeInsetsMake(0,0,0,0)];
[_slider setMinimumTrackImage:minImage forState:UIControlStateNormal];
[_slider setMaximumTrackImage:maxImage forState:UIControlStateNormal];
[_slider setThumbImage:thumbImageforState:UIControlStateNormal];
I can't Post an image because I don't have enough reputation. I'll try to explain a little better. I have a Thumb Image that is a circle. The center of the circle is transparent and you can see right to the background image of my view. The track for the slider continues all the way to the center of the circle. I'd prefer if the track stopped at the outline of the circle. I hope that makes more sense.
I have not received any suggestions as to how to hide the slider track when it is behind the thumb image so I am posting my solution incase someone else has this problem. If anyone has a better way of doing this please let me know.
In order to have a transparent thumb image and not have the track connect in the center of it I had to set the slider track images to blank images and setup my own slider track image that is just going to be resized every time the value of the slider changes. Not only will I be changing the width of the custom slider track but I also will have to update the X position of the image every time I resize it so that it will connect with the end image of my slider track.
In my case I have a slider that is set to 0 and its Max is set to the Width of the slider. My custom slider track is only going to appear on the right side of the thumb image. So as you slide the thumb image to the right the bar shrinks and the left side is clear.
First I setup my Slider Thumb Image and Set the Slider Track to Blank Images.
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *thumbImage =[UIImage imageNamed:#"answerSlider.png"];
[_slider setMinimumTrackImage:[UIImage new] forState:UIControlStateNormal];
[_slider setMaximumTrackImage:[UIImage new] forState:UIControlStateNormal];
[_slider setThumbImage:thumbImage forState:UIControlStateNormal];
thumbImage = nil;
_slider.value = 0;
}
In the ViewWillApear I have a call to setup my custom SLider track that I will create.
- (void)viewDidAppear:(BOOL)animated{
[self updateSlideBarLength];
}
Inside updateSLideBarLength I Use the value of the slider to help determine the width of my slider track Image.
- (void)updateSlideBarLength{
// Get Position Of Slider
CGFloat sliderValue = _slider.value;
CGFloat thumbImageWidth = 58; // this is the width of my thumb image
CGFloat sliderControlWidth = 288.0f; // This is a constant number in my case
CGFloat sliderWidth = sliderControlWidth - thumbImageWidth - sliderValue;
CGFloat sliderBarHeight = 2.0f;
// Reset Width Of aFrame
CGFloat sliderEndY = _sliderEnd.frame.origin.y;
CGFloat sliderEndX = _sliderEnd.frame.origin.x;
CGFloat sliderBarY = sliderEndY + (_sliderEnd.frame.size.height / 2 - 1);
CGFloat sliderBarX = sliderEndX - sliderWidth + (_sliderEnd.frame.size.width/2);
CGRect aframe = CGRectMake(sliderBarX, sliderBarY, sliderWidth, sliderBarHeight);
if (_sliderBar == nil) {
NSLog(#"Slider Bar Doesnt Exist");
_sliderBar = [[UIImageView alloc] initWithFrame:aframe];
_sliderBar.image = [UIImage imageNamed:#"sliderBar.png"];
[self.view addSubview:_sliderBar];
[self.view bringSubviewToFront:_sliderBar];
}
_sliderBar.frame = aframe;
}
Lastly I make sure the updateSliderBarLength is called every time the slider value changes.
- (IBAction)sliderMoved:(id)sender {
[self updateSlideBarLength];
}
This works although I had to adjust the Max value of my slider so that the custom slider track image I created always lines up with the edge of the thumb Image. While sliding the thumb image as the value increased the more the slider track started to infringe on my thumb image. By increasing the slider value I was able to stop this from happening. Again, I'm not convinced this is the best way to do this but it works for me. Later when I have more Rep and can post Pictures I will edit this post to help everyone visualize what I am describing.
I did something similar to Sean Marraffa, although I found it easier to subclass UISlider and update the frames of my image views in thumbRectForBounds:trackRect:value using something like this:
var trackHeight: CGFloat = 2
override func thumbRectForBounds(bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect
{
let superValue = super.thumbRectForBounds(bounds, trackRect: rect, value: value)
let originY = (bounds.height-trackHeight)/2.0
minTrackImageView?.frame = CGRectMake(0, originY, superValue.minX, trackHeight)
maxTrackImageView?.frame = CGRectMake(superValue.maxX, originY, bounds.width-superValue.maxX, trackHeight)
return superValue
}
minTrackImageView and maxTrackImageView are image views I set up in viewDidLoad.
In my app I use IKImageBrowserView with background. If wantsLayer NO - all fine and IKImageBrowserView look like nice. But if I enabled wantsLayer (in parent view) the background in IKImageBrowserView is corrupt. (Sorry English is not my native language and I can't find the correct word).
If I understand correctly, problem in this fragment. But I can't see where.
NSRect visibleRect = [owner visibleRect];
NSRect bounds = [owner bounds];
CGImageRef image = NULL;
NSString *path = [[NSBundle mainBundle] pathForResource:[#"metal_background.tif" stringByDeletingPathExtension] ofType:[#"metal_background.tif" pathExtension]];
if (!path) {
return;
}
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:path], NULL);
if (!imageSource) {
return;
}
image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
if (!image) {
CFRelease(imageSource);
return;
}
float width = (float) CGImageGetWidth(image);
float height = (float) CGImageGetHeight(image);
//compute coordinates to fill the view
float left, top, right, bottom;
top = bounds.size.height - NSMaxY(visibleRect);
top = fmod(top, height);
top = height - top;
right = NSMaxX(visibleRect);
bottom = -height;
// tile the image and take in account the offset to 'emulate' a scrolling background
for (top = visibleRect.size.height-top; top>bottom; top -= height){
for(left=0; left<right; left+=width){
CGContextDrawImage(context, CGRectMake(left, top, width, height), image);
}
}
CFRelease(imageSource);
CFRelease(image);
Image with problem
Image without problem
Thanks
I tried using IKImageView a few weeks ago and ran in to a number if problems. It's a nice class but it doesn't seem to have been updated in a long time. I doesn't support retina screen and, as you point out goes crazy when you add a layer.
I wanted to do some custom drawing with CALayer over the top of an image. The first thing I tried was to add a layer to the image view, which gave me problems. The solution I went for want to add a custom NSView subview to the image view, add auto layout constraints so that it always the same size, then add a layer hosting view to the subview. This worked fine, so move your drawing code to a custom subview and it should work.
IKImageView
LayerHostingView (<--- add CALayer here)
I ran in to this distorted background image problem as well and it turned out to be that I had set up the CALayer for the background incorrectly. I suspect that you are misunderstanding the cause to be setting wantsLayer to false because when that is set the layer does not immediately redraw new layers that you set.
I reached this conclusion as if I set up the layer correctly, then set wantsLayer false immediately before setting a bad CALayer in the next line of code I see the correct, undistorted image first and only when I scroll (and it redraws) do I see the buggy effects in your screenshot. However if I set wantsLayer true at the same point in code I see the buggy graphics immediately when the browser loads, without having to redraw first.
You can move the wantsLayer false statement further down your code to find the line that's breaking it, it'll be where the layer is reset/updated somewhere.
I have some troubles with CATextLayer, that could be due to me, but I didn't find any help on this topic. I am on OS X (on iOS it should be the same).
I create a CATextLayer layers with scale factor > 1 and what I get is a blurred text. The layer is rasterized before applying the scale, I think. Is this the expected behavior? I hope it is not, because it just makes no sense... A CAShapeLayer is rasterized after that its transformation matrix is applied, why the CATextLayer should be different?
In case I am doing something wrong... what is it??
CATextLayer *layer = [CATextLayer layer];
layer.string = #"I like what I am doing";
layer.font = (__bridge CFTypeRef)[NSFont systemFontOfSize:24];
layer.fontSize = 24;
layer.anchorPoint = CGPointZero;
layer.frame = CGRectMake(0, 0, 400, 100);
layer.foregroundColor = [NSColor blackColor].CGColor;
layer.transform = CATransform3DMakeScale(2., 2., 1.);
layer.shouldRasterize = NO;
[self.layer addSublayer:layer];
The solution I use at the moment is to set the contentsScale property of the layer to the scale factor. The problem is that this solution doesn't scale: if the scale factor of any of the parent layers changes, then contentsScale should be updated too. I should write code to traverse the layers tree to update the contentsScale properties of all CATextLayers... not exactly what I would like to do.
Another solution, that is not really a solution, is to convert the text to a shape and use a CAShapeLayer. But then I don't see the point of having CATextLayers.
A custom subclass of CALayer could help in solving this problem?
EDIT: Even CAGradientLayer is able to render its contents, like CAShapeLayer, after that its transformation matrix is applied. Can someone explain how it is possible?
EDIT 2: My guess is that paths and gradients are rendered as OpenGL display lists, so they are rasterized at the actual size on the screen by OpenGL itself. Texts are rasterized by Core Animation, so they are bitmaps for OpenGL.
I think that I will go with the contentsScale solution for the moment. Maybe, in the future, I will convert texts to shapes. In order to get best results with little work, this is the code I use now:
[CATransaction setDisableActions:YES];
CGFloat contentsScale = ceilf(scaleOfParentLayer);
// _scalableTextLayer is a CATextLayer
_scalableTextLayer.contentsScale = contentsScale;
[_scalableTextLayer displayIfNeeded];
[CATransaction setDisableActions:NO];
After trying all the approaches, the solution I am using now is a custom subclass of CALayer. I don't use CATextLayer at all.
I override the contentsScale property with this custom setter method:
- (void)setContentsScale:(CGFloat)cs
{
CGFloat scale = MAX(ceilf(cs), 1.); // never less than 1, always integer
if (scale != self.contentsScale) {
[super setContentsScale:scale];
[self setNeedsDisplay];
}
}
The value of the property is always rounded to the upper integer value. When the rounded value changes, then the layer must be redrawn.
The display method of my CALayer subclass creates a bitmap image of the size of the text multiplied by the contentsScale factor and by the screen scale factor.
- (void)display
{
CGFloat scale = self.contentsScale * [MyUtils screenScale];
CGFloat width = self.bounds.size.width * scale;
CGFloat height = self.bounds.size.height * scale;
CGContextRef bitmapContext = [MyUtils createBitmapContextWithSize:CGSizeMake(width, height)];
CGContextScaleCTM(bitmapContext, scale, scale);
CGContextSetShouldSmoothFonts(bitmapContext, 0);
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)(_text));
CGContextSetTextPosition(bitmapContext, 0., self.bounds.size.height-_ascender);
CTLineDraw(line, bitmapContext);
CFRelease(line);
CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
self.contents = (__bridge id)(image);
CGImageRelease(image);
CGContextRelease(bitmapContext);
}
When I change the scale factor of the root layer of my hierarchy, I loop on all text layers and set the contentsScale property to the same factor. The display method is called only if the rounded value of the scale factor changes (i.e. if the previous value was 1.6 and now I set 1.7, nothing happens. But if the new value is 2.1, then the layer is redisplayed).
The cost in terms of speed of the redraw is little. My test is to change continuously the scale factor of a hierarchy of 40 text layers on an 3rd gen. iPad. It works like butter.
CATextLayer is different because the underlying CoreText renders the glyphs with the specified font size (educated guess based on experiments).
You could add an action to the parent layer so as soon as it's scale changes, it changes the font size of the text layer.
Blurriness could also come from misaligned pixels. That can happen if you put the text layer to non integral position or any transformation in the superlayer hierarchy.
Alternatively you could subclass CALayer and then draw the text using Cocoa in drawInContext:
see example here:
http://lists.apple.com/archives/Cocoa-dev/2009/Jan/msg02300.html
http://people.omnigroup.com/bungi/TextDrawing-20090129.zip
If you want to have the exact behaviour of a CAShapeLayer then you will need to convert your string into a bezier path and have CAShapeLayer render it. It's a bit of work but then you will have the exact behaviour you are looking for. An alternate approach, is to scale the fontSize instead. This yields crisp text every time but it might not fit to you exact situation.
To draw text as CAShapeLayer have a look at Apple Sample Code "CoreAnimationText":
http://developer.apple.com/library/mac/#samplecode/CoreAnimationText/Listings/Readme_txt.html
I'm writing an app (XCode 4.6) that displays drawings of various different paths - for now it's just straight lines and bezier paths, but eventually it will get more complicated. I am currently not using any layers and the display is actually pretty simple.
my drawRect code looks like this:
- (void)drawRect:(CGRect)rect :(int) points :(drawingTypes) type //:(Boolean) initial
{
//CGRect bounds = [[UIScreen mainScreen] bounds];
CGRect appframe= [[UIScreen mainScreen] applicationFrame];
CGContextRef context = UIGraphicsGetCurrentContext();
_helper = [[Draw2DHelper alloc ] initWithBounds :appframe.size.width :appframe.size.height :type];
CGPoint startPoint = [_helper generatePoint] ;
[_uipath moveToPoint:startPoint];
[_uipath setLineWidth: 1.5];
CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGPoint center = CGPointMake(self.center.y, self.center.x) ;
[_helper createDrawing :type :_uipath :( (points>0) ? points : defaultPointCount) :center];
[_uipath stroke];
}
- (void)drawRect:(CGRect)rect
{
if (_uipath == NULL)
_uipath = [[UIBezierPath alloc] init];
else
[_uipath removeAllPoints];
[self drawRect:rect :self.graphPoints :self.drawingType ];
}
The actual path is generated by a helper object (_helper). I would like to animate the display of this path to appear slowly over a few seconds as it is being drawn - what is the easiest and fastest way of doing this?
What do you mean "appear slowly?" I assume you do not mean fade in all over at once, but rather mean that you want it to look like the path is being drawn with a pen?
To do that, do the following:
Create a CAShapeLayer the same size as your view and add it as a sublayer of your view.
Get the CGPath from your bezier path.
Install the CGPath into your shapeLayer.
Create a CABasicAnimation that animates the value of the shape layer's strokeEnd property from 0.0 to 1.0. That will cause the shape to be drawn as if it was being traced from beginning to end. You probably want a path that contains a single, contiguous sub-path. If you want it to circle back and connect, make it a closed path.
There are all kinds of cool tricks you can do with shape layers and animating changes to the path. and it's settings (like strokeStart and strokeEnd, stokeColor, fillColor, etc.) If you animate the path itself, you have to make sure the beginning path and ending path have the same number of control points internally. (And path arcs are tricky because the path has a different number of control points depending on the angle of the arc.)
I'm working on a Cocoa fullscreen application. I am using 1 NSView that has 1 CALayer that has multiple sublayers. Right now for testing - I am using any keystrokes to add dots (20 x 20 ) to the screen. This is just for testing of drawing the dots. My issue is that I am using a filter on my dot layers - specifically I am using CIDiscBlur - and once I reach about 30 dots - the drawing of the dots significantly slows down. There can be a 1 - 1.5 second delay between the key press and the appearance of the dot. I have noticed that if I remove setting the CIDisBlur filter on the layers - that there is no slow down.
Are there any best practices or tips I should be using when drawing this many sublayers? Any help would be great.
CIFilter *blurFilter = [CIFilter filterWithName:#"CIDiscBlur"];
[blurFilter setDefaults];
[blurFilter setValue:(id)[NSNumber numberWithFloat:15.0] forKey:#"inputRadius"];
dotFilters = [[NSArray arrayWithObjects:(id)blurFilter, nil] retain];
CGColorRef purpleColor = CGColorCreateGenericRGB(0.604, 0.247, 0.463, 1.0);
CALayer *dot = [[CALayer layer] retain];
dot.backgroundColor = purpleColor;
dot.cornerRadius = 15.0f;
dot.filters = dotFilters;
NSRect screenRect = [[self.window screen] frame];
// 10 point border around the screen
CGFloat width = screenRect.size.width - 20;
CGFloat height = screenRect.size.height - 20;
#define ARC4RANDOM_MAX 0x100000000
width = ((CGFloat)arc4random() / ARC4RANDOM_MAX) * width + 10;
height = ((CGFloat)arc4random() / ARC4RANDOM_MAX) * height + 10;
dot.frame = CGRectMake(width, height, 20,20);//30, 30);
[dot addSublayer:dotsLayer];
I also tried using masksToBounds = YES to see if that helped - but no luck.
You can probably get a performance gain by not using corner radius to make your round layers. While it's a nice little shortcut to just make a round layer in a static context, when you're animating, it will degrade performance significantly. You'd be better off specifying a circular path to a CAShapeLayer or dropping down to Core Graphics and just drawing a circle in the drawInContext call. To test if I'm right, just comment out your call to set the corner radius and apply your filter. See if that speeds things up. If not, then I'm not sure what's up. It may mean you'll have to find a different way to get your effect without a filter. If you'll always have the same look for your dots, you'll can probably "cheat" by using an image.
Best regards.