I have some code that sets a border around a UITextView. It builds correctly in one class; when I take that code and copy it to another class (changing the object name), it no longer builds, saying "Property 'borderWidth' cannot be found in forward class object 'CALayer *'" (the same message for the other two lines of code). I have done a clean, re-build and nothing helps. Why is this happening? and how do I fix it?
- (void)viewDidLoad {
[super viewDidLoad];
//-- draw box around notes field
orderNotes.layer.borderWidth = 1.0f;
orderNotes.layer.borderColor = [[UIColor blackColor] CGColor];
orderNotes.layer.cornerRadius = 4;
}
Object "orderNotes" is defined as UITextView. There are no other errors.
You need to:
#import <QuartzCore/QuartzCore.h>
Otherwise, the layer property is not visible to you.
Related
I have an array of UIImageViews. I want to apply a shadow to each of these images. I've used the code below:
- (void)awakeFromNib {
for (UIImageView *image in imagesJigsawPieces) {
image.layer.shadowColor = [UIColor blackColor].CGColor;
image.layer.shadowOffset = CGSizeMake(-1, -1);
image.layer.shadowOpacity = 1;
image.layer.shadowRadius = 5.0;
image.clipsToBounds = NO; //EDIT: I have also included this with no change
}
}
I have also included #import <QuartzCore/CALayer.h>.
I am not getting any errors but I am also not getting any shadows on my images.
Are you certain this code is being called? Have you placed a breakpoint in the for loop to verify?
-awakeFromNib is called only if you have a view (or whatever) in a nib file connected via IBOutlet to an ivar in your code. -awakefFromNib is called, in this case, instead of -initWithFrame: (or the like), an important distinction which I sometimes forget myself!
I'm trying to change background color of one of my UIView subclasses. For some reason self.backgroundColor = [UIColor whiteColor];doesn't do anything when I put it in my - (id)initWithFrame:(CGRect)framemethod inside the view. The view is always black. I have also tried self.myView.backgroundColor ... from my view's controller, but that didn't work either. Any ideas on what I'm doing wrong?
The relevant code looks like this:
[...]
#interface PaperView : UIView
[....]
[...]
#implementation PaperView
[...]
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[...]
// Initialization code
self.backgroundColor = [UIColor whiteColor]; // This doesn't do anything, the view is always black.
}
return self;
}
If this view is being unarchived from a xib, you need to override -initWithCoder:. -initWithFrame: is only invoked if you are creating your view programmatically.
I had this same problem. I hooked into layoutSubviews() and it worked ok:
override func layoutSubviews() {
super.layoutSubviews()
self.backgroundColor = UIColor.clearColor()
}
This is indicative of the view not having a frame set to it. I recommend setting a breakpoint in your initWithFrame: to verify that its being called. If you were to call, say, ... = [UIView alloc] init], then that could be the source of your problem.
EDIT
If initWithFrame: is in fact being called, it's possible that the view is being covered by another view giving the appearance that it's not working (since you don't see it) or that the view itself is hidden.
Another way to troubleshoot is to override the backgroundColor property and set a breakpoint. Find out what else, in the callstack, is changing the color.
You may use self.layer.backgroundColor instead:
mySubclassedView.layer.backgroundColor = UIColor.green.cgColor
In what method do you call self.myView.backgroundColor?
Are you sure, that it's after viewDidLoad: ?
But, have no idea what is wrong with your first method.
Could you show more code?
I just had this exact same problem. The background color did not show even though I set the correct frame and set the background color to white in my custom init method, as well as in my viewWillAppear method. I also verified that nothing was covering it.
Then I found the solution: I set the background color in viewDidAppear instead, and all was fine:
- (void)viewDidAppear:(BOOL)animated
{
[self.view setBackgroundColor:[UIColor whiteColor]];
self.view.frame = _viewFrame;
}
(The _viewFrame CGRect was passed in to my init method.)
Another option is to set it in - (void)viewDidLayoutSubviews, depending on when and how exactly you want to set your background color.
To be entirely honest, I don't understand (yet) why setting the background color in viewDidAppear worked while it didn't work in the init method, and the code for setting it was identical in both places.
Hope this helps,
Erik
UPDATE:
It does have something to do with view's frame. When I set my view's frame in my init method, then setting the background color in the viewDidAppear no longer has the desired effect. This is even the case if I set my view's frame after my view build method that creates the sub views. So the real mystery is: between the point where I am done creating my view and the point where it is displayed, what in the view's life cycle is causing the view's frame to be reset to something that is incorrect?
So, the answer really is: it will work as long as your frame is set correctly and your view is visible. Just check your view's frame throughout the view's lifecycle to make sure it's correct.
Tricky...
Why can't you implement self.backgroundColor = [UIColor whiteColor] in -viewDidLoad method instead of -initWithFrame? Then try self.backgroundColor = [UIColor whiteColor]; as well as self.myView.backgroundColor to see which works.
In my application, when the user clicks an infoButton, it should add another view to the screen at a specific location. I'm trying to achieve this behavior with the following method:
- (IBAction)showInfo1:(id)sender
{
UIView *myView1 = [[UIView alloc] initWithFrame:CGRectMake(25,25,50,20)];
[self.view addSubview:myView1];
}
(I declared everything in the header file of my class.)
When I run the code and press the button, nothing appears to happen (I don't see the new view).
I also noticed that XCode is displaying the following warning:
Local declaration of 'myView1' hides instance variable.
Does anyone have any ideas?
How do you know nothing changes?
It looks like myView1 doesn't actually contain anything. Try setting the background color of myView1
- (IBAction) showInfo1: (id) sender
{
UIView *myView1 = [[UIView alloc] initWithFrame:CGRectMake(25,25,50,20)];
myView1.backgroundColor = [UIColor redColor];
[self.view addSubview: myView1];
}
To open it at a specific point you need to change the parameters in CGRectMake() for example
to open it at the very top left with a width of 50 and height of 20 you would do:
CGRectMake(0, 0, 50, 20)
"Local declaration of 'myView1' hides instance variable." message appears because you have declared in your class some property with the same name (even if the type is different).
If you put UIView *myView1 in your class definition, your method would look like
- (IBAction)showInfo1:(id)sender{
myView1 = [[UIView alloc] initWithFrame:CGRectMake(25,25,50,20)];
[self.view addSubview:myView1];
}
Here comes more considerations: you must to release myView1 when you stop using it, avoid to be placed more than once, etc. but here we have the basic idea.
Finally, maybe your view is already added, but 'cause it doesn't contains anything yet, you don't notice it. Also you would like to check UIViewController to see if works better for you.
When an NSTextView is a subview of an NSView that is layer-backed (-wantsLayer == YES), it does not render the squiggly red underlines for misspelled words. All it takes to reproduce this is to make an empty Cocoa project, open the nib, drag NSTextView into the window, and toggle the window's content view to want a layer. Boom - no more red underlines.
I've done some searching, and this appears to be a known situation and has been true since 10.5. What I cannot find, though, is a workaround for it. Is there no way to get the underlines to render when NSTextView is in a layer-backed view?
I can imagine overriding NSTextView's drawRect: and using the layout manager to find the proper rects with the proper temporary attributes set that indicate misspellings and then drawing red squiggles myself, but that is of course a total hack. I also can imagine Apple fixing this in 10.7 (perhaps) and suddenly my app would have double underlines or something.
[update] My Workaround
My current workaround was inspired by nptacek's mentioned spell checking delegate method which prompted me to dig deeper down a path I didn't notice before, so I'm going to accept that answer but post what I've done for posterity and/or further discussion.
I am running 10.6.5. I have a subclass of NSTextView which is the document view of a custom subclass of NSClipView which in turn is a subview of my window's contentView which has layers turned on. In playing with this, I eventually had all customizations commented out and still the spelling checking was not working correctly.
I isolated what, I believe, are two distinct problems:
#1 is that NSTextView, when hosted in a layer-backed view, doesn't even bother to draw the misspelling underlines. (I gather based on Google searches that there may have been a time in the 10.5 days when it drew the underlines, but not in the correct spot - so Apple may have just disabled them entirely to avoid that problem in 10.6. I am not sure. There could also be some side effect of how I'm positioning things, etc. that caused them not to appear at all in my case. Presently unknown.)
#2 is that when NSTextView is in this layer-related situation, it appears to not correctly mark text as misspelled while you're typing it - even when -isContinuousSpellCheckingEnabled is set to YES. I verified this by implementing some of the spell checking delegate methods and watching as NSTextView sent messages about changes but never any notifying to set any text ranges as misspelled - even with obviously misspelled words that would show the red underline in TextEdit (and other text views in other apps). I also overrode NSTextView's -handleTextCheckingResults:forRange:types:options:orthography:wordCount: to see what it was seeing, and it saw the same thing there. It was as if NSTextView was actively setting the word under the cursor as not misspelled, and then when the user types a space or moves away from it or whatever, it didn't re-check for misspellings. I'm not entirely sure, though.
Okay, so to work around #1, I overrode -drawRect: in my custom NSTextView subclass to look like this:
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
[self drawFakeSpellingUnderlinesInRect:rect];
}
I then implemented -drawFakeSpellingUnderlinesInRect: to use the layoutManager to get the text ranges that contain the NSSpellingStateAttributeName as a temporary attribute and render a dot pattern reasonably close to the standard OSX misspelling dot pattern.
- (void)drawFakeSpellingUnderlinesInRect:(NSRect)rect
{
CGFloat lineDash[2] = {0.75, 3.25};
NSBezierPath *underlinePath = [NSBezierPath bezierPath];
[underlinePath setLineDash:lineDash count:2 phase:0];
[underlinePath setLineWidth:2];
[underlinePath setLineCapStyle:NSRoundLineCapStyle];
NSLayoutManager *layout = [self layoutManager];
NSRange checkRange = NSMakeRange(0,[[self string] length]);
while (checkRange.length > 0) {
NSRange effectiveRange = NSMakeRange(checkRange.location,0);
id spellingValue = [layout temporaryAttribute:NSSpellingStateAttributeName atCharacterIndex:checkRange.location longestEffectiveRange:&effectiveRange inRange:checkRange];
if (spellingValue) {
const NSInteger spellingFlag = [spellingValue intValue];
if ((spellingFlag & NSSpellingStateSpellingFlag) == NSSpellingStateSpellingFlag) {
NSUInteger count = 0;
const NSRectArray rects = [layout rectArrayForCharacterRange:effectiveRange withinSelectedCharacterRange:NSMakeRange(NSNotFound,0) inTextContainer:[self textContainer] rectCount:&count];
for (NSUInteger i=0; i<count; i++) {
if (NSIntersectsRect(rects[i], rect)) {
[underlinePath moveToPoint:NSMakePoint(rects[i].origin.x, rects[i].origin.y+rects[i].size.height-1.5)];
[underlinePath relativeLineToPoint:NSMakePoint(rects[i].size.width,0)];
}
}
}
}
checkRange.location = NSMaxRange(effectiveRange);
checkRange.length = [[self string] length] - checkRange.location;
}
[[NSColor redColor] setStroke];
[underlinePath stroke];
}
So after doing this, I can see red underlines but it doesn't seem to update the spelling state as I type. To work around that problem, I implemented the following evil hacks in my NSTextView subclass:
- (void)setNeedsFakeSpellCheck
{
if ([self isContinuousSpellCheckingEnabled]) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(forcedSpellCheck) object:nil];
[self performSelector:#selector(forcedSpellCheck) withObject:nil afterDelay:0.5];
}
}
- (void)didChangeText
{
[super didChangeText];
[self setNeedsFakeSpellCheck];
}
- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
{
[super updateInsertionPointStateAndRestartTimer:flag];
[self setNeedsFakeSpellCheck];
}
- (void)forcedSpellCheck
{
[self checkTextInRange:NSMakeRange(0,[[self string] length]) types:[self enabledTextCheckingTypes] options:nil];
}
It doesn't work quite the same way as the real, expected OSX behavior, but it's sorta close and it gets the job done for now. Hopefully this is helpful for someone else, or, better yet, someone comes here and tells me I was missing something incredibly simple and explains how to fix it. :)
Core Animation is awesome, except when it comes to text. I experienced this firsthand when I found out that subpixel antialiasing was not a given when working with layer-backed views (which you can technically get around by setting an opaque backgroundColor and making sure to draw the background). Subpixel anti-aliasing is just one of the many caveats encountered while working with text and layer-backed views.
In this case, you've got a couple of options. If at all possible, move away from layer-backed views for the parts of your program that utilize the text views. If you've already tried this, and can't avoid it, there is still hope!
Without going so far as overriding drawRect, you can achieve something that is close to the standard behavior with the following code:
- (NSArray *)textView:(NSTextView *)view didCheckTextInRange:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)options results:(NSArray *)results orthography:(NSOrthography *)orthography wordCount:(NSInteger)wordCount
{
for(NSTextCheckingResult *myResult in results){
if(myResult.resultType == NSTextCheckingTypeSpelling){
NSMutableDictionary *attr = [[NSMutableDictionary alloc] init];
[attr setObject:[NSColor redColor] forKey:NSUnderlineColorAttributeName];
[attr setObject:[NSNumber numberWithInt:(NSUnderlinePatternDot | NSUnderlineStyleThick | NSUnderlineByWordMask)] forKey:NSUnderlineStyleAttributeName];
[[inTextView layoutManager] setTemporaryAttributes:attr forCharacterRange:myResult.range];
[attr release];
}
}
return results;
}
We're basically doing a quick-and-dirty delegate method for NSTextView (make sure to set the delegate in IB!) which checks to see if a word is flagged as incorrect, and if so, sets a colored underline.
Note that there are some issues with this code -- Namely that characters with descenders (g, j, p, q, y, for example) won't display the underline correctly, and it's only been tested for spelling errors (no grammar checking here!). The underline dot pattern (NSUnderlinePatternDot) does not match Apple's style for spellchecking, and the code is still enabled even when layer backing is disabled for the view. Additionally, I'm sure there are other problems, as this code is quick and dirty, and hasn't been checked for memory management or anything else.
Good luck with your endeavor, file bug reports with Apple, and hopefully this will someday be a thing of the past!
This is also a bit of a hack, but the only thing I could get working was to put an intermediate delegate on the NSTextView's layer, so that all selectors are passed through, but drawLayer:inContext: then calls the NSTextView's drawRect:. This works, and is probably a little more future proof, although I'm not sure if it will break any CALayer animations. It also seems you have to fix the CGContextRef's CTM (based on the backing layer frame?).
Edit:
You can get the drawing rect as in the drawInContext: documentation, with CGContextGetClipBoundingBox(ctx), but there might be an issue with flipped coordinates in the NSTextView.
I'm not entirely sure how to fix this as calling drawRect: as I did is a bit hackish, but I'm sure someone on the net has a tutorial on doing it. Perhaps I can make one if/when I have time and work it out.
It might be worthwhile looking for an NSCell backing the NSTextView, as it's probably a lot more appropriate to use this instead.
I'm trying to use the select and copy feature of the IKImageView. If all you want to do is have an app with an image, select a portion and copy it to the clipboard, it's easy. You set the copy menu pick to the first responder's copy:(id) method and magically everything works.
However, if you want something more complicated, like you want to copy as part of some other operation, I can't seem to find the method to do this.
IKImageView doesn't seem to have a copy method, it doesn't seem to have a method that will even tell you the selected rectangle!
I have gone through Hillegass' book, so I understand how the clipboard works, just not how to get the portion of the image out of the view...
Now, I'm starting to think that I made a mistake in basing my project on IKImageView, but it's what Preview is built on (or so I've read), so I figured it had to be stable... and anyway, now it's too late, I'm too deep in this to start over...
So, other than not using IKImageView, any suggestions on how to copy the select region to the clipboard manually?
EDIT actually, I have found the copy(id) method, but when I call it, I get
<Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 16 bits/pixel; 1-component color space; kCGImageAlphaPremultipliedLast; 2624 bytes/row.
Which obviously doesn't happen when I do a normal copy through the first-responder... I understand the error message, but I'm not sure where it's getting those parameters from...
Is there any way to trace through this and see how this is happening? A debugger won't help for obvious reasons, as well as the fact that I'm doing this in Mozilla, so a debugger isn't an option anyway...
EDIT 2 It occurs to me that the copy:(id) method I found may be copying the VIEW rather than copying a chunk of the image to the clipboard, which is what I need.
The reason I thought it was the clipboard copy is that in another project, where I'm copying from an IKImageView to the clipboard straight from the edit menu, it just sends a copy:(id) to the firstResponder, but I'm not actually sure what the firstresponder does with it...
EDIT 3 It appears that the CGBitmapContextCreate error is coming from [imageView image] which, oddly enough, IS a documented method.
It's possible that this is happening because I'm putting the image in there with a setImage:(id) method, passing it an NSImage*... Is there some other, more clever way of getting an NSImage into an IKImageView?
The -copy: method in IKImageView does what every other -copy: method does: it copies the current selection to the clipboard. It is, however, implemented as a private method in IKImageView for some reason.
You can just call it directly:
[imageView copy:nil];
This will copy whatever is currently selected to the clipboard.
I don't think there's a way to directly access the image content of the current selection in IKImageView using public methods, this is a good candidate for a bug report/feature request.
You can, however, use the private method -selectionRect to get a CGRect of the current selection and use that to extract the selected portion of the image:
//stop the compiler from complaining when we call a private method
#interface IKImageView (CompilerSTFU)
- (CGRect)selectionRect
#end
#implementation YourController
//imageView is an IBOutlet connected to your IKImageView
- (NSImage*)selectedImage
{
//get the current selection
CGRect selection = [imageView selectionRect];
//get the portion of the image that the selection defines
CGImageRef selectedImage = CGImageCreateWithImageInRect([imageView image],(CGRect)selection);
//convert it to an NSBitmapImageRep
NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:selectedImage] autorelease];
CGImageRelease(selectedImage);
//create an image from the bitmap data
NSImage* image = [[[NSImage alloc] initWithData:[bitmap TIFFRepresentation]] autorelease];
//in 10.6 you can skip converting to an NSBitmapImageRep by doing this:
//NSImage* image = [[NSImage alloc] initWithCGImage:selectedImage size:NSZeroSize];
return image;
}
#end
Ok, so the copy: nil fails, and the [imageView image] fails, but it turns out that I have another copy of the NSImage from when I added it into the view in the first place, so I could that. Also, CGImageCreateWithImageInRect expects a CGImageRef not an NSImage*, so I had to do some conversions.
In addition, for some reason the selection rectangle is flipped, either it's bottom origined, and the image is top, or the other way around, so I had to flip it.
And for some reason, the compiler suddenly started complaining that NSRect isn't the same type as CGRect (Which implies that it suddenly went from 32 to 64 bit or something... not sure why...)
Anyway, here is my copy of selectedImage:
- (NSImage*)selectedImage
{
//get the current selection
CGRect selection = flipCGRect(imageView, [imageView selectionRect]);
//get the portion of the image that the selection defines
struct CGImage * full = [[doc currentImage] CGImageForProposedRect: NULL context: NULL hints: NULL];
CGImageRef selectedImage = CGImageCreateWithImageInRect( full, selection);
//convert it to an NSBitmapImageRep
NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:selectedImage] autorelease];
CGImageRelease(selectedImage);
// //create an image from the bitmap data
NSImage* image = [[[NSImage alloc] initWithData:[bitmap TIFFRepresentation]] autorelease];
// //in 10.6 you can skip converting to an NSBitmapImageRep by doing this:
//NSImage* image = [[NSImage alloc] initWithCGImage:selectedImage size:NSZeroSize];
return image;
}
I wrote flipCGRect, and [doc currentImage] returns an NSImage*...