Core Image help: Setting CIImage from UIImage via UIImagePicker (then filtering it) - xcode

Searched around on SO for an answer. Found some interesting stuff but I am still stuck.
In a view controller I have a UIImageView which I set an image taken from UIImagePicker when the view loads.
(I also set a CIImage at the same time)
I have two sliders (one for brightness, one for contrast). Upon moving the slider, a filter is applied to the CIImage, then the CIImage is rendered into the the UIImageView's UIImage.
Fist off, the UIImage taken from UIImagePicker does show up correctly when first selected.
I also have my slider ranges set correctly and have verified the proper float values are being passed to the delegate functions (via NSLog).
However, when I try playing with the sliders, my UIImageView turns white! Perhaps you folks can help. I'll post some code snippets:
First, my #interface:
#interface PrepViewController : UIViewController <UIImagePickerControllerDelegate , UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UIImageView *editingImage;
#property (weak, nonatomic) CIImage *editingCIImage;
#property (weak, nonatomic) CIContext *editingCIContext;
#property (weak, nonatomic) CIFilter *editingCIFilter;
#property (nonatomic) BOOL didAskForImage;
#property (weak, nonatomic) IBOutlet UISlider *brightnessSlider;
#property (weak, nonatomic) IBOutlet UISlider *contrastSlider;
- (void) doImageFilter:(NSString *)filter withValue:(float)value;
#end
My viewDidLoad, pretty simple just sets didAskForImage to NO
- (void)viewDidLoad
{
[super viewDidLoad];
self.didAskForImage = NO;
}
viewDidAppear is where I check if I've already asked for the image, then ask for it
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if(!self.didAskForImage){
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
sourceType = UIImagePickerControllerSourceTypeCamera;
}
picker.sourceType = sourceType;
[self presentModalViewController:picker animated:YES];
}
}
Here is the imagePickerController didFinishPickingImage delegate method
this is where I actually set the UIImageView's image property and initialize the CIImage, CIContext, and CIFilter objects.
I also set the didAskForImage boolean and the slider targets.
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingImage : (UIImage *)image
editingInfo:(NSDictionary *)editingInfo
{
self.didAskForImage = YES;
self.editingImage.image = image;
self.editingCIImage = [[CIImage alloc] initWithImage:image];
self.editingCIContext = [CIContext contextWithOptions:nil];
self.editingCIContext = [CIContext contextWithOptions:nil];
self.editingCIFilter = [CIFilter filterWithName:#"CIColorControls" keysAndValues:kCIInputImageKey, self.editingCIImage, nil];
[self.contrastSlider addTarget:self action:#selector(contrastMove:) forControlEvents:UIControlEventValueChanged];
[self.brightnessSlider addTarget:self action:#selector(brightnessMove:) forControlEvents:UIControlEventValueChanged];
[picker dismissModalViewControllerAnimated:YES];
}
and the cancel delegate
-(void)imagePickerControllerDidCancel:(UIImagePickerController *) picker
{
[picker dismissModalViewControllerAnimated:YES];
picker = nil;
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
}
Here are the sliders' delegate methods. Again, the NSLogs display the expected values.
- (void) contrastMove:(id)sender{
NSLog(#"%f", [(UISlider *)sender value]);
[self doImageFilter:#"inputContrast" withValue:[(UISlider *)sender value]];
}
- (void) brightnessMove:(id)sender{
NSLog(#"%f", [(UISlider *)sender value]);
[self doImageFilter:#"inputBrightness" withValue:[(UISlider *)sender value]];
}
And finally, here is the doImageFilter method I created, which actually does the filter and reassigns the UIImageView (this is where it turns white)
- (void) doImageFilter:(NSString *)filter withValue:(float)value{
[self.editingCIFilter setValue:[NSNumber numberWithFloat:value] forKey:filter];
CIImage *output = [self.editingCIFilter outputImage];
CGImageRef cgi = [self.editingCIContext createCGImage:output fromRect:[output extent]];
self.editingCIImage = output;
self.editingImage.image = [UIImage imageWithCGImage:cgi];
}
So I have a hunch that the CIImage isn't even being initialized properly in the first place; but I don't know why. Been at this for a couple hours now.
Thanks for all your help!
Cheers

Hahaha, oh wow.
So I figured it out, my CIContext and CIFilter need to be set to strong instead of weak.
Awesome.

Related

Load UIView defined in XIB into UIView

following a post presented here, I tried to load a view defined in a XIB file into a view, currently being displayed. Basically, what I want to do is to replace the middle view (see screenshot below) with a different view when the user clicks on the button.
This is the simple view I want to load into the middle view:
And this is the source code of the ViewController:
ViewController.h
#import
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *loadxibButton;
#property (strong, nonatomic) IBOutlet UIView *middleView;
- (IBAction)buttonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)buttonClicked:(id)sender {
NSLog(#"click...");
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"NewMiddleContent" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
self.middleView= nibView;
}
#end
When I click on the button, nothing happens, i.e. the new view will not be displayed. Can anybody please help?
Thanks a lot
Christian
Phillip, thanks a lot!
Instead of
- (IBAction)buttonClicked:(id)sender {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"NewMiddleContent" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
self.middleView= nibView;
}
I now use this code:
- (IBAction)buttonClicked:(id)sender {
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"NewMiddleContent" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
nibView.frame = CGRectMake(261, 0, 532, 748);
[[[self view] viewWithTag:2] removeFromSuperview];
[[self view] addSubview:nibView];
}
Is this want you meant, i.e. is this like "best practice"?
Bye
Christian
Your view controller has a view property which, presumably, has three subviews (left, middle, and right). Instead of just setting a middleView property in your controller, your strategy should be to update the primary view's subviews array (removing the one that's to be replaced and inserting the new one).

Button getting EXC_BAD_ACCESS by method with ivars

I'm neophyte in obj-c, so I cant understand some of this logic. I want to understand my code and app logic. My app is modification of easy example with animation of UIImageView: this's .h (in standart view-based template)
#import <UIKit/UIKit.h>
#interface ImageAnimatorViewController : UIViewController {
UIImageView *animImage;
}
#property (nonatomic, retain) IBOutlet UIImageView *animImage;
- (IBAction)startAnimation;
- (NSArray*)creatAnimation:(NSString*)fileName;
#end
this's .m (without standart dealloc and viewDidUnload)
#synthesize animImage;
- (IBAction)startAnimation{
[animImage startAnimating];
}
- (void)viewDidLoad{
[super viewDidLoad];
animImage.animationImages = [self creatAnimation:#"Images.jpg"];
animImage.animationDuration = 1.0f;
animImage.animationRepeatCount = 0;
}
- (NSArray*)creatAnimation:(NSString*)fileName{
UIImage *image = [UIImage imageNamed:fileName];
NSMutableArray *animationImages = [NSMutableArray array];
for (int i = 0; i<8; i++) {
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage,
CGRectMake(i*600.0f, 0.0f, 600.0f, 262.0f));
UIImage *animationImage = [UIImage imageWithCGImage:imageRef];
[animationImages addObject:animationImage];
[animationImage release];
}
return animationImages;
}
Also i've xib with imageview and button, but when i pressed b there's crash and EXC_BAD_ACCESS in console. In IBAction I understand imageView hasn't animationImages, but why? and has strange reference count = 3,because in viewDidLoad has 2(and why 2?). I add self and self-> prefix in viewDidLoad, but it's no result. animationImages is (copy) property,so images saves after autorelease pool is drained with animationImages. I'm very surprised about this behavior of app.10x!
Do not release animationImage in createAnimation method. You also should clean memory for CGImageRef using CGImageRelease

UIWebView Delegate Code Doesn't Seem Right

I've implemented sample code using the UIWebView that I found, but it doesn't look right to me, though it works. Specifically because it sets the UIWevView delegate twice (in viewDidLoad and viewWillAppear). Also, myWebView is set as an autorelease object, but then it's released in dealoc. I'm hoping someone can tell me how to clean this up.
// *** WebViewController.h ***
#interface WebViewController : UIViewController
<UIWebViewDelegate>
{
UIWebView *myWebView;
UIActivityIndicatorView *activityIndicator;
}
#property (nonatomic, retain) UIWebView *myWebView;
#property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
#end
// *** WebViewController.m ***
#synthesize myWebView;
- (void) viewDidLoad {
[super viewDidLoad];
// - - - - -> Create the UIWebView
CGRect webFrame = [[UIScreen mainScreen] applicationFrame];
webFrame.origin.y += 42.0;
webFrame.size.height -= 106.0;
self.myWebView = [[[UIWebView alloc] initWithFrame:webFrame] autorelease];
self.myWebView.backgroundColor = [UIColor whiteColor];
self.myWebView.scalesPageToFit = YES;
self.myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.myWebView.delegate = self;
[self.view addSubview: self.myWebView];
// - - - - -> Create the UIActivityIndicatorView
self.activityIndicator = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
[self.view addSubview: self.activityIndicator];
self.activityIndicator.center = CGPointMake(135,438);
[self.myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.someURL.com/"]]];
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.myWebView.delegate = self;
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.myWebView stopLoading];
self.myWebView.delegate = nil;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void) dealloc {
myWebView.delegate = nil;
[myWebView release];
[activityIndicator release];
[super dealloc];
}
It's pretty much fine to do that. The idea is pretty basic. You do not want the delegate to receive any delegation messages when off screen. So the idea of setting the delegate -viewWillAppear: and -viewWillDisappear: is correct. But I don't see any delegate methods being implemented in the code.

How do I put a background image on a text area (IN THE IPAD)

How do I put a template'd background image on a text area?
Not sure what you mean with "template'd"... but....
In interface builder, add the imageview behind the textview, uncheck the "opaque" box for the textview.
You can set the contents property of the text views layer.
#import <QuartzCore/QuartzCore.h>
...
- (void) viewDidLoad {
[super viewDidLoad];
textView.layer.contents = (id)[UIImage imageNamed:#"myImage.png"].CGImage
}
should work
If you want to add the image programmaticaly I managed to do it this way:
First in my textviewViewController.h I added an outlet for the UITextView.
#import <UIKit/UIKit.h>
#interface textviewViewController : UIViewController {
UITextView *textView;
}
#property (nonatomic, retain) IBOutlet UITextView *textView;
#end
Next, in my .xib I added my UITextView and connected my outlet.
Lastly in the viewDidLoad of textviewViewController.m I add the image as a subview to my textview.
- (void)viewDidLoad {
[super viewDidLoad];
// Change the pathForResource to reflect the name and type of your image file
NSString *path = [[NSBundle mainBundle] pathForResource:#"d" ofType:#"jpg"];
UIImage *img = [UIImage imageWithContentsOfFile:path];
UIImageView *imgView = [[UIImageView alloc] initWithImage:img];
[self.textView insertSubview:imgView atIndex:0];
[imgView release];
}
I tried the insertSubview:atIndex: with both 0 and 1 with the same result, but I would stick with 0 idicating that it's behind the textview.

Archiving and Unarchiving results in Bad Access

I'm having trouble setting up a model object to save the visual state of user generated CALayers in a simple graphics application for the iphone.
I'm attempting to save the background color and frame of all the current layers on screen by passing those properties to model objects which implement the NSCoding protocol and then into an NSMutableArray which the app delegate owns. Then I archive the array with NSKeyedArchiver and store it in the NSUserDefaults.
Each CALayer's backgroundColor property is converted to a UIColor to be encoded by the model object for storage. I think that I'm unarchiving the array incorrectly or not restoring state from the unarchived array correctly. When I attempt to access the UIColor object that was store in the model object, I get an EXC_BAD_ACCESS.
I thought it was possibly a bug with encoding UIColor objects so tried pulling the values out of the CGColorRef with the CGColorGetComponents function and storing them in an array to encode and archive, but I had the same result of bad access after unarchiving, so I think I'm just doing it wrong.
This is my model object:
#interface AILayerData : NSObject <NSCoding> {
UIColor* color;
CGRect frame;
}
#property (retain) UIColor* color;
#property (assign) CGRect frame;
#end
#implementation AILayerData
#synthesize color;
#synthesize frame;
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:color forKey:#"color"];
[coder encodeCGRect:frame forKey:#"frame"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[AILayerData alloc] init];
if (self != nil)
{
color = [coder decodeObjectForKey:#"color"];
frame = [coder decodeCGRectForKey:#"frame"];
}
return self;
}
#end
And this is my archiving implementation:
#implementation AppDelegate
- (void)applicationWillTerminate:(UIApplication *)application {
NSArray *layersArray = viewController.view.layer.sublayers;
dataArray = [NSMutableArray array];
for(AILayer *layer in layersArray)
{
AILayerData *layerData = [[AILayerData alloc] init];
layerData.frame = layer.frame;
UIColor *layerColor = [UIColor colorWithCGColor:layer.backgroundColor];
layerData.color = layerColor;
[dataArray addObject:layerData];
[layerData release];
}
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:layerDataArray] forKey:#"savedArray"];
}
#end
And here is where I restore state:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
spaceView = [[AISpaceView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.view = spaceView;
[spaceView release];
spaceView.delegate = self;
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"savedArray"];
if (dataRepresentingSavedArray != nil) {
[self restoreStateWithData:dataRepresentingSavedArray];
}
}
- (void)restoreStateWithData:(NSData *)data
{
NSArray *savedLayers = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (savedLayers != nil) {
NSArray *restoredLayers = [[NSArray alloc] initWithArray:savedLayers];
for(AILayerData *layerDataObject in restoredLayers) {
UIColor *layerColor = layerDataObject.color;
AILayer *newLayer = [[AILayer alloc] init];
newLayer.backgroundColor = layerColor.CGColor;
newLayer.frame = layerDataObject.frame;
newLayer.isSelected = NO;
[self.view.layer addSublayer:newLayer];
[newLayer release];
}
[restoredLayers release];
[spaceView.layer layoutSublayers];
}
}
#end
Any help with this is greatly appreciated. I'm pretty much a noob. I was encoding, archiving and unarching an NSArray of NSNumbers converted from the color's floats in pretty much the same way and getting bad access.
You certainly want to retain the color in initWithCoder:
color = [[coder decodeObjectForKey:#"color"] retain];
or, with the dot syntax as color was declared as a retain property:
self.color = [coder decodeObjectForKey:#"color"];
You are over-releasing layerColor: You don't own it (layerDataObject does), but you are releasing it.
It looks like NSCoder for iPhone doesn't respond to -encodeWithCGRect:
Source: http://17.254.2.129/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSCoder_Class/Reference/NSCoder.html

Resources