This issue started happening in iOS7 with the new UIPickerView controller. To use images in your UIPickerView controller you must use the delegate method to return an image:
pickerView:viewForRow:forComponent:reusingView:
The problem is that the screen subsequently exhibits all kinds of strange behavior - the image views disappear as you move your finger up and down the control.
This is a solution posted on a dev forum which works as of iOS 7.0.2:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
// self.myImages is an array of UIImageView objects
UIView * myView = [self.myImages objectAtIndex:row];
// first convert to a UIImage
UIGraphicsBeginImageContextWithOptions(myView.bounds.size, NO, 0);
[myView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// then convert back to a UIImageView and return it
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
return imageView;
}
There is a far simpler way to do it than Ed Trujilo's method (It assumes you are using UIImageView's however ... Ed's method should work for any UIView, I believe).
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
return [[UIImageView alloc] initWithImage: [mpSelections[row] image]];
}
Related
I know how to create an NSImage depicting an NSView and all its subviews, but what I'm after is an NSImage of a view ignoring its subviews. I can think of ways of doing this with a subclass of NSView, but I'm keen to avoid subclassing if possible. Does anyone have any ideas?
Hide the subviews, grab the image, unhide the subviews:
NSMutableArray* hiddenViews = [[NSMutableArray] alloc init];
for (NSView* subview in [self subviews]) {
if (subview hidden) [hiddenViews addObject: subview];
else [subview setHidden:YES];
}
NSSize imgSize = self.bounds.size;
NSBitmapImageRep * bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
[bir setSize:imgSize];
[self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir];
NSImage* image = [[NSImage alloc] initWithSize:imgSize];
[image addRepresentation:bir];
for (NSView* subview in [self subviews]) {
if (![hiddenViews containsObject: subview])
[subview setHidden:NO];
}
I would suggest making a copy of the desired NSView offscreen and taking a snapshot of that.
I want to perform self.scrollview zoomToRect:animated
such that the rect is relative to the UIImage and not the UIImageView embedded inside that scrollView
How can I do this?
So the problem was converting the coordinates from the UIImage to the UIScrollView
This library helped a lot.
so added a method in my class
-(void)zoomToRectInImage:(CGRect)rect
{
CGRect r = [self.imageView convertRectFromImage:rect];
CGRect zoomrect = [self convertRect:r fromView:self.imageView];
[self zoomToRect:zoomrect animated:YES];
}
I need to assign a view to an NSMenuItem and do some custom drawing. Basically, I'm adding a little delete button next to the currently selected menu item, among other things. But I want my custom menu item to look and behave like a regular menu item in all other ways. According to the doc:
A menu item with a view does not draw
its title, state, font, or other
standard drawing attributes, and
assigns drawing responsibility
entirely to the view.
Ok, so I had to duplicate the look of the state column and the selection gradient, which wasn't that hard. The part I'm having trouble with is the way the menu item "flashes" or "blinks" after it is selected. I'm using an NSTimer to try to mimic this little animation, but it just feels off. How many times does it blink? What time interval should I use? I've experimented a lot and it just feels out of whack.
Has anyone done this before or have other suggestions on how to add a button to a menu item? Maybe there should be a stack exchange site just for custom cocoa drawing...
I know this is over a year old, but this was the first hit on my Google search and was unanswered, so I'm posting my answer for sake of those still looking for a solution.
For my app, I used Core Animation with a custom NSView for the NSMenuItem view. I created a new layer-backed view, set the background color, and added it to my custom view. I then animated the layer (the flashing part). Then in the -(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag callback, I removed the overlay and closed the menu. This doesn't perfectly match the default NSMenu's flash, but I wanted a 37Signals/Stack Overflow Yellow Fade Technique, so it works for me. Here it is in code:
-(void) mouseUp:(NSEvent *)theEvent {
CALayer *layer = [CALayer layer];
[layer setDelegate:self];
[layer setBackgroundColor:CGColorCreateGenericRGB(0.0, 0.0, 1.0, 1.0)];
selectionOverlayView = [[NSView alloc] init];
[selectionOverlayView setWantsLayer:YES];
[selectionOverlayView setFrame:self.frame];
[selectionOverlayView setLayer:layer];
[[selectionOverlayView layer] setNeedsDisplay];
[selectionOverlayView setAlphaValue:0.0];
[self addSubview:selectionOverlayView];
CABasicAnimation *alphaAnimation1 = [CABasicAnimation animationWithKeyPath: #"alphaValue"];
alphaAnimation1.beginTime = 0.0;
alphaAnimation1.fromValue = [NSNumber numberWithFloat: 0.0];
alphaAnimation1.toValue = [NSNumber numberWithFloat: 1.0];
alphaAnimation1.duration = 0.07;
CABasicAnimation *alphaAnimation2 = [CABasicAnimation animationWithKeyPath: #"alphaValue"];
alphaAnimation2.beginTime = 0.07;
alphaAnimation2.fromValue = [NSNumber numberWithFloat: 1.0];
alphaAnimation2.toValue = [NSNumber numberWithFloat: 0.0];
alphaAnimation2.duration = 0.07;
CAAnimationGroup *selectionAnimation = [CAAnimationGroup animation];
selectionAnimation.delegate = self;
selectionAnimation.animations = [NSArray arrayWithObjects:alphaAnimation1, alphaAnimation2, nil];
selectionAnimation.duration = 0.14;
[selectionOverlayView setAnimations:[NSDictionary dictionaryWithObject:selectionAnimation forKey:#"frameOrigin"]];
[[selectionOverlayView animator] setFrame:[selectionOverlayView frame]];
}
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
[selectionOverlayView removeFromSuperview];
NSMenuItem *enclosingMenuItem = [self enclosingMenuItem];
NSMenu *enclosingMenu = [enclosingMenuItem menu];
[enclosingMenu cancelTracking];
[enclosingMenu performActionForItemAtIndex:[enclosingMenu indexOfItem:enclosingMenuItem]];
}
It is actually possible to have your custom view flash like a regular NSMenuItem without implementing the animation manually.
Note: this uses a private API and also fixes a handful of other strange NSMenuItem quirks related to custom views.
NSMenuItem.h
#import <AppKit/AppKit.h>
#interface NSMenuItem ()
- (BOOL)_viewHandlesEvents;
#end
Bridging Header
#import "NSMenuItem.h"
MenuItem.swift
class MenuItem: NSMenuItem {
override func _viewHandlesEvents() -> Bool {
return false
}
}
This API really ought to be public, and if you're not developing for the App Store, it might be worth having a look at.
Here is my code that flashes a custom menu item.
int16_t fireTimes;
BOOL isSelected;
- (void)mouseEntered:(NSEvent*)event
{
isSelected = YES;
}
- (void)mouseUp:(NSEvent*)event {
fireTimes = 0;
isSelected = !isSelected;
[self setNeedsDisplay:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:#selector(animateDismiss:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
}
-(void)animateDismiss:(NSTimer *)aTimer
{
if (fireTimes <= 2) {
isSelected = !isSelected;
[self setNeedsDisplay:YES];
} else {
[aTimer invalidate];
[self sendAction];
}
fireTimes++;
}
- (void)drawRect:(NSRect)dirtyRect {
if (isSelected) {
NSRect frame = NSInsetRect([self frame], -4.0f, -4.0f);
[[NSColor selectedMenuItemColor] set];
NSRectFill(frame);
[itemNameFld setTextColor:[NSColor whiteColor]];
} else {
[itemNameFld setTextColor:[NSColor blackColor]];
}
}
- (void)sendAction
{
NSMenuItem *actualMenuItem = [self enclosingMenuItem];
[NSApp sendAction:[actualMenuItem action] to:[actualMenuItem target] from:actualMenuItem];
NSMenu *menu = [actualMenuItem menu];
[menu cancelTracking];
// [self setNeedsDisplay:YES]; // I'm not sure of this
}
Greetings all,
I am a noob and I have been trying to work through this for a few days.
I am adding images to a view via UItouch. The view contains a background on top of which the new images are add. How do I clear the images I am adding from the subview, without getting rid of the UIImage that is the background. Any assistance is greatly appreciated. Thanks in Advance.
here is the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event {
NSUInteger numTaps = [[touches anyObject] tapCount];
if (numTaps==2) {
imageCounter.text =#"two taps registered";
//__ remove images
UIView* subview;
while ((subview = [[self.view subviews] lastObject]) != nil)
[subview removeFromSuperview];
return;
}else {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
CGRect myImageRect = CGRectMake((touchPoint.x -40), (touchPoint.y -45), 80.0f, 90.0f);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"pg6_dog_button.png"]];
myImage.opaque = YES; // explicitly opaque for performance
[self.view addSubview:myImage];
[myImage release];
[imagesArray addObject:myImage];
NSNumber *arrayCount =[self.view.subviews count];
viewArrayCount.text =[NSString stringWithFormat:#"%d",arrayCount];
imageCount=imageCount++;
imageCounter.text =[NSString stringWithFormat:#"%d",imageCount];
}
}
What you need is a way of distinguishing the added UIImageView objects from the background UIImageView. There are two ways I can think of to do this.
Approach 1: Assign added UIImageView objects a special tag value
Each UIView object has a tag property which is simply an integer value that can be used to identify that view. You could set the tag value of each added view to 7 like this:
myImage.tag = 7;
Then, to remove the added views, you could step through all of the subviews and only remove the ones with a tag value of 7:
for (UIView *subview in [self.view subviews]) {
if (subview.tag == 7) {
[subview removeFromSuperview];
}
}
Approach 2: Remember the background view
Another approach is to keep a reference to the background view so you can distinguish it from the added views. Make an IBOutlet for the background UIImageView and assign it the usual way in Interface Builder. Then, before removing a subview, just make sure it's not the background view.
for (UIView *subview in [self.view subviews]) {
if (subview != self.backgroundImageView) {
[subview removeFromSuperview];
}
}
A more swiftly code for approach #1 in only one functional line of code :
self.view.subviews.filter({$0.tag == 7}).forEach({$0.removeFromSuperview()})
I'm trying to subclass NSScroller in order to draw my own scroller knob. To do this, I've subclassex NSScrollView and usex the following code to instantiate my custom NSScrollers:
- (void)awakeFromNib;
{
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
NSString *scrollBarVariant = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain] valueForKey:#"AppleScrollBarVariant"];
if (![scrollBarVariant isEqualToString:#"DoubleBoth"]) {
[self setVerticalScroller:[[[TRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
[self setHorizontalScroller:[[[TRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
}
This works and my NSScrollers display correctly. But I'm occasionally seeing rendering issues upon first loading my application. Within Interface Builder I have laid out a number of NSScrollViews with their scrollbars set to hide automatically. The issue I'm seeing is that when the application first loads, the scrollbar backgrounds are rendered across the NSScrollViews contents.
alt text http://www.freeimagehosting.net/uploads/1d3fc75db8.png
I believe this is because I instantiate my NSScroll subclass (TRSubclass) via awakeFromNib, which means that the scrollbars are given the frame of the NSScrollView before it is automatically resized to meet the windows saved location and size (in other words, it's using the frame that's assigned by default within Interface Builder). What's the best way around this?
I've tried forcing the NSScrollView to redisplay (using setNeedsDisplay: and display:) but with no luck. Has anyone else come across a similar issue?
I'm using the same schema in my applications and I fighted this issues a lot. I use the same trick: scrollers are substituted in [scrollView awakeFromNib] methods, but I don't face such rendering issues at the moment. You can try to play with "draws background" property of the NSScrollView - it really helps sometimes
- (void)changeSubs
{
// change clip view
// ...
// change scrollers
NSRect horizontalScrollerFrame = [[self horizontalScroller] frame];
NSRect verticalScrollerFrame = [[self verticalScroller] frame];
if (![[self verticalScroller] isKindOfClass:[CRScroller class]])
[self setVerticalScroller:[[[CRScroller alloc] initWithFrame:verticalScrollerFrame] autorelease]];
if (![[self horizontalScroller] isKindOfClass:[CRScroller class]])
[self setHorizontalScroller:[[[CRScroller alloc] initWithFrame:horizontalScrollerFrame] autorelease]];
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self changeSubs];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSKeyedUnarchiver* unpacker = (id)aDecoder;
[unpacker setClass:[CRClipView class] forClassName:[NSClipView className]];
[unpacker setClass:[CRScroller class] forClassName:[NSScroller className]];
self = [super initWithCoder:aDecoder];
if (self)
{
}
return self;
}
- (void)awakeFromNib
{
[self changeSubs];
}
There are few tricks here, they work depending on a way NSScrollView is created. 'isKindOfClass' check helps to avoid double-swap.