How can i change the brightness of an Image with slider using GPUImageBrightnessFilter ?
I tried,
-(void)updateBrightness {
GPUImageFilter *selectedFilter = nil;
[selectedFilter removeAllTargets];
selectedFilter = [[GPUImageFilter alloc] init];
CGFloat midpoint = [(UISlider *)sender value];
[(GPUImageBrightnessFilter *)settingsFilter setBrightness:midpoint];
UIImage *filteredImage = [selectedFilter imageByFilteringImage:_image_view.image];
fx_imageView.image = filteredImage;
}
There are several problems with the above code.
First, you're not actually using a brightness filter against your image, because you're calling -imageByFilteringImage: on selectedFilter, which is a generic GPUImageFilter that you allocated fresh. Your GPUImageBrightnessFilter of settingsFilter is never used.
Second, you don't want to be allocating a new filter with every time you update a parameter. Allocate your GPUImageBrightnessFilter once and simply update it as values change.
Third, you don't want to keep re-filtering UIImages. Going to and from UIImages is a slow process (and won't work properly when using -imageByFilteringImage: on the same filter, because of some caching I do). Instead, create a GPUImagePicture based on your original image, add a GPUImageBrightnessFilter to that as a target, and target your GPUImageBrightnessFilter at a GPUImageView. Use -processImage every time you update your brightness filter and your updates will be much, much faster. When you need to extract your final image, use -imageFromCurrentlyProcessedOutput.
these may help you...
-(void)viewDidLoad
{
[sliderChange setMinimumValue:-0.5];
[sliderChange setMaximumValue:0.5];
[sliderChange setValue:0.0];
brightnessFilter = [[GPUImageBrightnessFilter alloc] init];
}
-(IBAction)upDateSliderValue:(id)sender
{
GPUImagePicture *fx_image;
fx_image = [[GPUImagePicture alloc] initWithImage:originalImage];
[brightnessFilter setBrightness:self.sliderChange.value];
[fx_image addTarget:brightnessFilter];
[fx_image processImage];
UIImage *final_image = [brightnessFilter imageFromCurrentlyProcessedOutput];
self.selectedImageView.image = final_image;
}
I am learning how to use NSCopy. I want to make a copy of a custom object I am using, which is an ImageView in a UIScrollView.
I am trying to implement NSCopying protocol as follows :
-(id)copyWithZone:(NSZone *)zone
{
ImageView *another = [[ImageView allocWithZone:zone]init];
another.imageView = self.imageView;
another.imageToShow = self.imageToShow;
another.currentOrientation = self.currentOrientation;
another.portraitEnlarge = self.portraitEnlarge;
another.landscapeEnlarge = self.landscapeEnlarge;
another.originalFrame = self.originalFrame;
another.isZoomed = self.isZoomed;
another.shouldEnlarge = self.shouldEnlarge;
another.shouldReduce = self.shouldReduce;
another.frame = self.frame;
//another.delegate = another;
another.isZoomed = NO;
[another addSubview:another.imageView];
return another;
}
Then to copy the object in another class :
ImageView * onePre = [pictureArray objectAtIndex:0];
ImageView * one = [onePre copy];
The copy is made however I have one odd problem. The copied object's ImageView (a UIImageView) and ImageToShow (a UIImage) properties seem to be the same as the original objects. This kind of makes sense as in the copy code I am re-pointing a pointer, rather than making a new version of ImageView and ImageToShow.
My question is how do I make a copy of an object that contains pointers to other objects ?
Thanks !
UIView does not conform to NSCopying, but it does conform to NSCoding:
another.imageView = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:self.imageView]];
This serializes and then deserializes the object, which is the standard way to perform a deep copy in ObjC.
EDIT: See https://stackoverflow.com/a/13664732/97337 for an example of a common -clone category method that uses this.
Im have a subclass of UIView, called PinView, which contains an image. Basically PinView gets added multiple times to my app, and then I perform a transform on PinView objects. The issue is that when I add a lot of PinViews, my app gets sluggish because I am transforming each PinView.
Ideally, I want to have one 'static' PinView that gets added to a UIView multiple times but i only have to transform it once. Currently this doesn't seem to work. Every time I add the static PinView to my UIView, it will only ever appear in the UIView once (due to it only being able to have one superview I'm guessing).
Im struggle to find out the best way to go about solving this problem - how do I use a single pointer to a PinView, add it multiple times to a UIView and be able to perform a transform on the PinView which gets passed on to PinViews displayed in my UIView? (by the way, the transform is always the same for all the PinViews).
Im assuming this will be the best way to get a performance improvement, if this is not the case please let me know.
UPDATE:
- (void)layoutSubviews
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue);
NSMutableArray *mut = nil;
PinView *pinView = nil;
CallOutView *callOut = nil;
//get all dictionary entries
for(NSString *identifier in self.annotationsDict.allKeys){
//get the array from dictionary
mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy];
//change layout if not nil
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = ((PinView *)[mut objectAtIndex:PIN]);
pinView.transform = transform;
[mut replaceObjectAtIndex:PIN withObject:pinView];
}
if([[mut objectAtIndex:CALL_OUT] isKindOfClass:[CallOutView class]]){
callOut = ((CallOutView *)[mut objectAtIndex:CALL_OUT]);
callOut.transform = transform;
[mut replaceObjectAtIndex:CALL_OUT withObject:callOut];
if(pinView !=nil)callOut.center = CGPointMake(pinView.center.x, pinView.center.y - pinView.frame.size.height);
}
[self updateAnnotationsKey:identifier forObject:[NSArray arrayWithArray:mut]];
mut = nil;
pinView = nil;
callOut = nil;
}
}
UPDATE:
Removed the above and now just have:
- (void)layoutSubviews
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0/self.zoomValue, 1.0/self.zoomValue);
for(UIView *view in self.subviews){
view.transform = transform;
}
}
This can't be done I'm afraid. Each UIView instance can only be added to the screen once.
If all your views have similar transforms, you might have more luck using something like CAReplicatorLayer, which is a system for automatically creating duplicates of CALayers with different transforms.
That will only works if your views are all arranged in a grid or circle or something though. If they are just dotted randomly, it won't help.
If you are trying to draw more than 100 views, you're probably just bumping up against the fundamental performance ceiling of Core Animation on iOS.
The next approach to try would be to use OpenGL to draw your pins, perhaps using a library like Sparrow or Cocos2D to simplify drawing multiple transformed images with OpenGL (I'd recommend Sparrow as it integrates better with other UIKit controls - Cocos is more appropriate for games).
UPDATE:
This code is unnecessary:
mut = [(NSArray *)([self.annotationsDict objectForKey:identifier]) mutableCopy];
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = ((PinView *)[mut objectAtIndex:PIN]);
pinView.transform = transform;
[mut replaceObjectAtIndex:PIN withObject:pinView];
}
The code below is sufficient, because setting the transform doesn't modify the pointer to the object, so it will update the object in the array even if the array isn't mutable, and array objects are declared as 'id' so they don't need to be cast if you assign them to a variable of a known type.
mut = [self.annotationsDict objectForKey:identifier];
if([[mut objectAtIndex:PIN] isKindOfClass:[PinView class]]){
pinView = [mut objectAtIndex:PIN];
pinView.transform = transform;
}
I would also think you can remove the isKindOfClass: check if you only ever use those array indices for those object types. It may seem like a good precaution, but it carries a performance penalty if you're doing it over and over in a loop.
But for 10 views, I just wouldn't expect this to be that slow at all. Have you tried it without moving the callout centres. Does that perform better? If so, can you limit that to just the callouts that are currently visible, or move them using CGAffineTransformTranslate instead of setting the centre (which may be a bit quicker).
I've added a gradient layer:
[theView.layer insertSublayer:gradient atIndex:0];
And later on in another method I want to remove this layer. I figured I should get the array of sublayers then get the sublayer at index 0 and call removeFromSuperlayer on it. Is this the correct way or if not can you do it?
Cheers.
You can do it the way you described but it isn't so reliable. The problem is that if you do anything with the sublayers in between the addition and removal, the index of the sublayer can change and you end up removing something you didn't want to.
The best thing is to keep a reference to that layer and later when you want to remove it just call [theLayer removeFromSuperlayer]
Hope it helps
Funfunfun...
There are two layer properties you can use (in either case you have to iterate over the layers):
CALayer.name "is used by some layout managers to identify a layer". Set it to something reasonably guaranteed to be unique (e.g. "MyClassName.gradient").
CALayer.style is a dictionary. You can use keys which aren't used by CoreAnimation (e.g. NSMutableDictionary * d = [NSMutableDictionary dictionaryWithDictionary:layer.style]; [d setValue:[NSNumber numberWithBool:YES] forKey:#"MyClassName.gradient"]; layer.style = d;). This might be useful to associate arbitrary data with a view (such as the index path of the cell containing a text field...).
(I'm assuming that [NSDictionary dictionaryWithDictionary:nil] returns the empty dictionary instead of returning nil or throwing an exception. The corresponding thing is true for [NSArray arrayWithArray:nil].)
However, the extra code complexity, performance penalty, and chance of getting it wrong probably outweigh the small decrease in memory usage. 4 bytes per view is not that much if you have a handful of views (and even if you have loads, 4 bytes is the memory used by a single pixel!).
Swift has some really simple solutions for this:
// SWIFT 4 update
func removeSublayer(_ view: UIView, layerIndex index: Int) {
guard let sublayers = view.layer.sublayers else {
print("The view does not have any sublayers.")
return
}
if sublayers.count > index {
view.layer.sublayers!.remove(at: index)
} else {
print("There are not enough sublayers to remove that index.")
}
}
// Call like so
removeSublayer(view, layerIndex: 0)
Just remember, the sublayers are treated as an array, so if you have a count of 2, then 2 == 1 in the index, hence removeAtIndex(1).
There are a whole heap of options available for editing sublayers. Simply stop typing after sublayers!. and check them out.
Old Post.. but this may be helpful for someone...
My implementation of removing/replacing a CALayer. Using the Calayer.name as tc. describes above.
CAGradientLayer *btnGradient = [CAGradientLayer layer];
btnGradient.frame = button.bounds;
btnGradient.name = #"gradient";
btnGradient.colors = nil;
btnGradient.colors = [NSArray arrayWithObjects:
(id)[[VOHelper getButtonColor:kSchemeBorder] CGColor],
(id)[[VOHelper getButtonColor:kSchemeButton] CGColor],
nil];
if ([[[[button.layer sublayers] objectAtIndex:0] name] isEqualToString:btnGradient.name])
{
[button.layer replaceSublayer:[[button.layer sublayers] objectAtIndex:0] with:btnGradient];
}
else
{
[button.layer insertSublayer:btnGradient atIndex:0];
}
Swift 4
self.view.layer.sublayers = self.view.layer.sublayers?.filter { theLayer in
!theLayer.isKind(of: CAGradientLayer.classForCoder())
}
This worked for me Swift 5
if let _ = self.layer.sublayers?.first as? CAGradientLayer {
print("Debug: Gradient sublayer Found.")
self.layer.sublayers?[0] = gradient
}
else {
print("Debug: Non Gradient Layer Found in Sublayer 0.")
self.layer.insertSublayer(gradient, at: 0)
}
I've got a layer with some complex drawing code in its -drawInContext: method. I'm trying to minimize the amount of drawing I need to do, so I'm using -setNeedsDisplayInRect: to update just the changed parts. This is working splendidly. However, when the graphics system updates my layer, it's transitioning from the old to the new image using a cross-fade. I'd like it to switch over instantly.
I've tried using CATransaction to turn off actions and set the duration to zero, and neither work. Here's the code I'm using:
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];
Is there a different method on CATransaction I should use instead (I also tried -setValue:forKey: with kCATransactionDisableActions, same result).
You can do this by setting the actions dictionary on the layer to return [NSNull null] as an animation for the appropriate key. For example, I use
NSDictionary *newActions = #{
#"onOrderIn": [NSNull null],
#"onOrderOut": [NSNull null],
#"sublayers": [NSNull null],
#"contents": [NSNull null],
#"bounds": [NSNull null]
};
layer.actions = newActions;
to disable fade in / out animations on insertion or change of sublayers within one of my layers, as well as changes in the size and contents of the layer. I believe the contents key is the one you're looking for in order to prevent the crossfade on updated drawing.
Swift version:
let newActions = [
"onOrderIn": NSNull(),
"onOrderOut": NSNull(),
"sublayers": NSNull(),
"contents": NSNull(),
"bounds": NSNull(),
]
Also:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
//foo
[CATransaction commit];
When you change the property of a layer, CA usually creates an implicit transaction object to animate the change. If you do not want to animate the change, you can disable implicit animations by creating an explicit transaction and setting its kCATransactionDisableActions property to true.
Objective-C
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// change properties here without animation
[CATransaction commit];
Swift
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()
In addition to Brad Larson's answer: for custom layers (that are created by you) you can use delegation instead of modifying layer's actions dictionary. This approach is more dynamic and may be more performant. And it allows disabling all implicit animations without having to list all animatable keys.
Unfortunately, it's impossible to use UIViews as custom layer delegates, because each UIView is already a delegate of its own layer. But you can use a simple helper class like this:
#interface MyLayerDelegate : NSObject
#property (nonatomic, assign) BOOL disableImplicitAnimations;
#end
#implementation MyLayerDelegate
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
if (self.disableImplicitAnimations)
return (id)[NSNull null]; // disable all implicit animations
else return nil; // allow implicit animations
// you can also test specific key names; for example, to disable bounds animation:
// if ([event isEqualToString:#"bounds"]) return (id)[NSNull null];
}
#end
Usage (inside the view):
MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init];
// assign to a strong property, because CALayer's "delegate" property is weak
self.myLayerDelegate = delegate;
self.myLayer = [CALayer layer];
self.myLayer.delegate = delegate;
// ...
self.myLayerDelegate.disableImplicitAnimations = YES;
self.myLayer.position = (CGPoint){.x = 10, .y = 42}; // will not animate
// ...
self.myLayerDelegate.disableImplicitAnimations = NO;
self.myLayer.position = (CGPoint){.x = 0, .y = 0}; // will animate
Sometimes it's convenient to have view's controller as a delegate for view's custom sublayers; in this case there is no need for a helper class, you can implement actionForLayer:forKey: method right inside the controller.
Important note: don't try to modify the delegate of UIView's underlying layer (e.g. to enable implicit animations) — bad things will happen :)
Note: if you want to animate (not disable animation for) layer redraws, it is useless to put [CALayer setNeedsDisplayInRect:] call inside a CATransaction, because actual redrawing may (and probably will) happen sometimes later. The good approach is to use custom properties, as described in this answer.
Here's a more efficient solution, similar to accepted answer but for Swift. For some cases it will be better than creating a transaction every time you modify the value which is a performance concern as others have mentioned e.g. common use-case of dragging the layer position around at 60fps.
// Disable implicit position animation.
layer.actions = ["position": NSNull()]
See apple's docs for how layer actions are resolved. Implementing the delegate would skip one more level in the cascade but in my case that was too messy due to the caveat about the delegate needing to be set to the associated UIView.
Edit: Updated thanks to the commenter pointing out that NSNull conforms to CAAction.
Actually, I didn't find any of the answers to be the right one. The method that solves the problem for me was this:
- (id<CAAction>)actionForKey:(NSString *)event {
return nil;
}
Then you can whatever logic in it, to disable a specific animation, but since I wanted to removed them all, I returned nil.
Based on Sam's answer, and Simon's difficulties... add the delegate reference after creating the CSShapeLayer:
CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.
... elsewhere in the "m" file...
Essentially the same as Sam's without the ability to toggle via the custom "disableImplicitAnimations" variable arrangement. More of a "hard-wire" approach.
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
// disable all implicit animations
return (id)[NSNull null];
// allow implicit animations
// return nil;
// you can also test specific key names; for example, to disable bounds animation:
// if ([event isEqualToString:#"bounds"]) return (id)[NSNull null];
}
To disable implicit layer animations in Swift
CATransaction.setDisableActions(true)
Found out a simpler method to disable action inside a CATransaction that internally calls setValue:forKey: for the kCATransactionDisableActions key:
[CATransaction setDisableActions:YES];
Swift:
CATransaction.setDisableActions(true)
Updated for swift and disabling only one implicit property animation in iOS not MacOS
// Disable the implicit animation for changes to position
override open class func defaultAction(forKey event: String) -> CAAction? {
if event == #keyPath(position) {
return NSNull()
}
return super.defaultAction(forKey: event)
}
Another example, in this case eliminating two implicit animations.
class RepairedGradientLayer: CAGradientLayer {
// Totally ELIMINATE idiotic implicit animations, in this example when
// we hide or move the gradient layer
override open class func defaultAction(forKey event: String) -> CAAction? {
if event == #keyPath(position) {
return NSNull()
}
if event == #keyPath(isHidden) {
return NSNull()
}
return super.defaultAction(forKey: event)
}
}
Add this to your custom class where you are implementing -drawRect() method. Make changes to code to suite your needs, for me 'opacity' did the trick to stop cross-fade animation.
-(id<CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)key
{
NSLog(#"key: %#", key);
if([key isEqualToString:#"opacity"])
{
return (id<CAAction>)[NSNull null];
}
return [super actionForLayer:layer forKey:key];
}
If you ever need a very quick (but admittedly hacky) fix it might be worth just doing (Swift):
let layer = CALayer()
// set other properties
// ...
layer.speed = 999
As of iOS 7 there's a convenience method that does just this:
[UIView performWithoutAnimation:^{
// apply changes
}];
To disable the annoying (blurry) animation when changing the string property of a CATextLayer, you can do this:
class CANullAction: CAAction {
private static let CA_ANIMATION_CONTENTS = "contents"
#objc
func runActionForKey(event: String, object anObject: AnyObject, arguments dict: [NSObject : AnyObject]?) {
// Do nothing.
}
}
and then use it like so (don't forget to set up your CATextLayer properly, e.g. the correct font, etc.):
caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()]
You can see my complete setup of CATextLayer here:
private let systemFont16 = UIFont.systemFontOfSize(16.0)
caTextLayer = CATextLayer()
caTextLayer.foregroundColor = UIColor.blackColor().CGColor
caTextLayer.font = CGFontCreateWithFontName(systemFont16.fontName)
caTextLayer.fontSize = systemFont16.pointSize
caTextLayer.alignmentMode = kCAAlignmentCenter
caTextLayer.drawsAsynchronously = false
caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()]
caTextLayer.contentsScale = UIScreen.mainScreen().scale
caTextLayer.frame = CGRectMake(playbackTimeImage.layer.bounds.origin.x, ((playbackTimeImage.layer.bounds.height - playbackTimeLayer.fontSize) / 2), playbackTimeImage.layer.bounds.width, playbackTimeLayer.fontSize * 1.2)
uiImageTarget.layer.addSublayer(caTextLayer)
caTextLayer.string = "The text you want to display"
Now you can update caTextLayer.string as much as you want =)
Inspired by this, and this answer.
Try this.
let layer = CALayer()
layer.delegate = hoo // Same lifecycle UIView instance.
Warning
If you set delegate of UITableView instance, sometimes happen crash.(Probably scrollview's hittest called recursively.)