xcode Removing Some Subviews from view - xcode

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()})

Related

iOS7 UIPickerView doesn't properly display custom views with images

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]];
}

adding subviews to an NSView to have a chess-like grid

I am trying to create a Cocoa UI that consists of two sets of squares (chess-like grids) that will assume different colours while an underlying algorithm is running. When the execution of the algorithm comes to an end, the UI should be able to handle clicks, panning and other gestures.
The hierarchy I have so far is the following (please check the attached code for specifics):
1) the main window that is the window of a window controller
2) a split view with two custom views, mainView and sideView (each one would hold a set of squares)
3) two view controllers (mainViewController and sideViewController)
I would like to be able to load the squares as subviews of mainView and sideView.
I thought of having another custom view, say SquareView with another nib file. My questions would be:
a) how do I create this SquareView so that it can be used to create the squares that will be added to mainView and sideView as subviews to form chess-like grids?
b) how do I add subviews to mainView and sideView to built the two grids? For the sake of simplicity, let's assume there would be four non-overlapping squares for each of the previously mentioned views.
Thank you!
MainView.m
#import "MainView.h"
#implementation MainView
- (void)drawRect:(NSRect)TheRect
{
[[NSColor grayColor] set];
[NSBezierPath fillRect:[self bounds]];
}
SideView.m
#import "SideView.h"
#implementation MainView
- (void)drawRect:(NSRect)TheRect
{
[[NSColor whiteColor] set];
[NSBezierPath fillRect:[self bounds]];
}
MainWindowController.h
#import <Cocoa/Cocoa.h>
#class SideViewController;
#class MainViewController;
#interface MainWindowController : NSWindowController
{
IBOutlet NSSplitView* oMainSplitView;
SideViewController* sideViewController;
MainViewController* mainViewController;
}
#end
MainWindowController.m
#import "MainWindowController.h"
#import "SideViewController.h"
#import "MainViewController.h"
#implementation MainWindowController
- (void)windowDidLoad
{
sideViewController = [[SideViewController alloc] initWithNibName:#"SideView" bundle:nil];
NSView* splitViewLeftView = [[oMainSplitView subviews] objectAtIndex:0];
NSView* sideView = [sideViewController view];
[sideView setFrame:[splitViewLeftView bounds]];
[sideView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[splitViewLeftView addSubview:sideView];
mainViewController = [[MainViewController alloc] initWithNibName:#"MainView" bundle:nil];
NSView* splitViewRightView = [[oMainSplitView subviews] objectAtIndex:1];
NSView* mainView = [mainViewController view];
[mainView setFrame:[splitViewRightView bounds]];
[mainView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[splitViewRightView addSubview:mainView];
}
You can make this as simple or as complicated as you desire: simple? do everything you want in MainView's drawRect method; complex: nest NSViews (or NSCell's, or NSBox's, etc.) and have each one draw itself.
Personally, I'd vote to keep it simple…
a) I think the easiest way would be to create a matrix of NSBoxes, which you could do in code or in IB. Having the squares in a matrix would make it easy to loop through them or access specific ones.
b) I'm not sure what your question is here -- you would do it just as you did in your posted code, using [mainView addSubview:squareMatrix];
After Edit: Actually, it looks like IB won't let you embed NSBoxes in a matrix. In the past, I've made a matrix of subclassed NSButtonCells (to allow background color with no border) that had a grid of 64x64 cells that were clickable and would change color with those clicks. I don't know if you want a fixed number of cells in your views, or do you need to dynamically change the number? Something like this could work for you I think -- I actually created this in code, because IB was really slow in updating with that many cells.
Here is what I did. In my case, I needed cells with no border but with background color, so I had to subclass NSButtonCell, like this:
-(id)initWithRGBAlpha:(NSArray *)rgbAlpha {
if (self == [super init]) {
NSColor *color = [NSColor colorWithCalibratedRed:[[rgbAlpha objectAtIndex:0]doubleValue]
green:[[rgbAlpha objectAtIndex:1]doubleValue]
blue:[[rgbAlpha objectAtIndex:2]doubleValue]
alpha:[[rgbAlpha objectAtIndex:3]doubleValue]];
[self setBackgroundColor:color];
[self setTitle:#""];
[self setBordered:NO];
[self setTag:0];
[self setImageScaling:3];
return self;
}else{
return nil;
}
}
-(void) setState:(NSInteger)value {
if (value == 1) {
self.backgroundColor = self.selectedColor;
[super setState:value];
}else {
self.backgroundColor = self.backgroundColor;
[super setState:value];
}
}
-(void) setBackgroundColor:(NSColor *)color {
backgroundColor = color;
selectedColor = [color colorWithAlphaComponent:.75];
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[super encodeWithCoder:encoder];
[encoder encodeObject:self.backgroundColor forKey:#"bColor"];
}
- (id)initWithCoder:(NSCoder *)decoder {
[super initWithCoder:decoder];
self.backgroundColor = [decoder decodeObjectForKey:#"bColor"];
return self;
}
I created the matrix in code, like so:
#implementation RDMatrix
-(void) initWithParentView:(NSView *) cv {
NSNumber *one = [NSNumber numberWithInt:1];
NSArray *colors = [NSArray arrayWithObjects:one,one,one,one,nil];
RDButtonCell *theCell = [[RDButtonCell alloc ]initWithRGBAlpha:colors];
[self initWithFrame:NSMakeRect(200,100,1,1) mode:2 prototype:theCell numberOfRows:64 numberOfColumns:64];
[self setSelectionByRect:TRUE];
[self setCellSize:NSMakeSize(8,8)];
[self sizeToCells];
self.target = self;
self.action = #selector(matrixClick:);
self.backgroundColor = [NSColor lightGrayColor];
self.drawsBackground = TRUE;
self.autoresizingMask = 8;
self.allowsEmptySelection = TRUE;
[cv addSubview:self];
}
-(void) matrixClick: (id) sender {
for (RDButtonCell *aCell in self.selectedCells){
if ([self.selectedCells count] < 64) {
aCell.backgroundColor = [NSColor colorWithCalibratedRed:1 green:0 blue:0 alpha:1];
}else{
aCell.backgroundColor = [NSColor colorWithCalibratedRed:0 green:.5 blue:1 alpha:1];
}
}
[self deselectAllCells];
}
#end

UIBarButtonItem frame? [duplicate]

UIBarButtonItem does not extend UIView, so there is nothing like a frame property.
But is there any way I can get what is it's CGRect frame, relative to the application UIWindow?
Do you like to use private APIs? If yes,
UIView* view = thatItem.view;
return [view convertRect:view.bounds toView:nil];
Of course no one wants this when targeting the AppStore. A more unreliable method, and also uses undocumented features, but will pass Apple's test, is to loop through the subviews to look for the corresponding button item.
NSMutableArray* buttons = [[NSMutableArray alloc] init];
for (UIControl* btn in theToolbarOrNavbar.subviews)
if ([btn isKindOfClass:[UIControl class]])
[buttons addObject:btn];
UIView* view = [buttons objectAtIndex:index];
[buttons release];
return [view convertRect:view.bounds toView:nil];
The index is the index to your bar item in the array of .items, after removing all blank items. This assumes the buttons are arranged in increasing order, which may not be. A more reliable method is to sort the buttons array in increasing .origin.x value. Of course this still assumes the bar button item must inherit the UIControl class, and are direct subviews of the toolbar/nav-bar, which again may not be.
As you can see, there are a lot of uncertainty when dealing with undocumented features. However, you just want to pop up something under the finger right? The UIBarButtonItem's .action can be a selector of the form:
-(void)buttonClicked:(UIBarButtonItem*)sender event:(UIEvent*)event;
note the event argument — you can obtain the position of touch with
[[event.allTouches anyObject] locationInView:theWindow]
or the button view with
[[event.allTouches anyObject] view]
Therefore, there's no need to iterate the subviews or use undocumented features for what you want to do.
I didn't see this option posted (which in my opinion is much simpler), so here it is:
UIView *barButtonView = [barButtonItem valueForKey:#"view"];
In iOS 3.2, there's a much easier way to show an Action Sheet popover from a toolbar button. Merely do something like this:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIActionSheet *popupSheet;
// Prepare your action sheet
[popupSheet showFromBarButtonItem:sender animated:YES];
}
This is the implementation I use for my WEPopover project: (https://github.com/werner77/WEPopover):
#implementation UIBarButtonItem(WEPopover)
- (CGRect)frameInView:(UIView *)v {
UIView *theView = self.customView;
if (!theView.superview && [self respondsToSelector:#selector(view)]) {
theView = [self performSelector:#selector(view)];
}
UIView *parentView = theView.superview;
NSArray *subviews = parentView.subviews;
NSUInteger indexOfView = [subviews indexOfObject:theView];
NSUInteger subviewCount = subviews.count;
if (subviewCount > 0 && indexOfView != NSNotFound) {
UIView *button = [parentView.subviews objectAtIndex:indexOfView];
return [button convertRect:button.bounds toView:v];
} else {
return CGRectZero;
}
}
#end
As long as UIBarButtonItem (and UITabBarItem) does not inherit from UIView—for historical reasons UIBarItem inherits from NSObject—this craziness continues (as of this writing, iOS 8.2 and counting ... )
The best answer in this thread is obviously #KennyTM's. Don't be silly and use the private API to find the view.
Here's a oneline Swift solution to get an origin.x sorted array (like Kenny's answer suggests):
let buttonFrames = myToolbar.subviews.filter({
$0 is UIControl
}).sorted({
$0.frame.origin.x < $1.frame.origin.x
}).map({
$0.convertRect($0.bounds, toView:nil)
})
The array is now origin.x sorted with the UIBarButtonItem frames.
(If you feel the need to read more about other people's struggles with UIBarButtonItem, I recommend Ash Furrow's blog post from 2012: Exploring UIBarButtonItem)
I was able to get Werner Altewischer's WEpopover to work by passing up the toolbar along with the
UIBarButton:
Mod is in WEPopoverController.m
- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item toolBar:(UIToolbar *)toolBar
permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections
animated:(BOOL)animated
{
self.currentUIControl = nil;
self.currentView = nil;
self.currentBarButtonItem = item;
self.currentArrowDirections = arrowDirections;
self.currentToolBar = toolBar;
UIView *v = [self keyView];
UIButton *button = nil;
for (UIView *subview in toolBar.subviews)
{
if ([[subview class].description isEqualToString:#"UIToolbarButton"])
{
for (id target in [(UIButton *)subview allTargets])
{
if (target == item)
{
button = (UIButton *)subview;
break;
}
}
if (button != nil) break;
}
}
CGRect rect = [button.superview convertRect:button.frame toView:v];
[self presentPopoverFromRect:rect inView:v permittedArrowDirections:arrowDirections animated:animated];
}
-(CGRect) getBarItemRc :(UIBarButtonItem *)item{
UIView *view = [item valueForKey:#"view"];
return [view frame];
}
You can get it from the UINavigationBar view. The navigationBar is a UIView which has 2 or 3 custom subviews for the parts on the bar.
If you know that the UIBarButtonItem is currently shown in the navbar on the right, you can get its frame from navbar's subviews array.
First you need the navigationBar which you can get from the navigationController which you can get from the UIViewController. Then find the right most subview:
UINavigationBar* navbar = curViewController.navigationController.navigationBar;
UIView* rightView = nil;
for (UIView* v in navbar.subviews) {
if (rightView==nil) {
rightView = v;
} else if (v.frame.origin.x > rightView.frame.origin.x) {
rightView = v; // this view is further right
}
}
// at this point rightView contains the right most subview of the navbar
I haven't compiled this code so YMMV.
This is not the best solution and from some point of view it's not right solution and we can't do like follow because we access to object inside UIBarBattonItem implicitly, but you can try to do something like:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[button setImage:[UIImage imageNamed:#"Menu_Icon"] forState:UIControlStateNormal];
[button addTarget:self action:#selector(didPressitem) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = item;
CGPoint point = [self.view convertPoint:button.center fromView:(UIView *)self.navigationItem.rightBarButtonItem];
//this is like view because we use UIButton like "base" obj for
//UIBarButtonItem, but u should note that UIBarButtonItem base class
//is NSObject class not UIView class, for hiding warning we implicity
//cast UIBarButtonItem created with UIButton to UIView
NSLog(#"point %#", NSStringFromCGPoint(point));
as result i got next:
point {289, 22}
Before implement this code, be sure to call [window makeKeyAndVisible] in your Applition delegate application:didFinishLaunchingWithOptions: method!
- (void) someMethod
{
CGRect rect = [barButtonItem convertRect:barButtonItem.customview.bounds toView:[self keyView]];
}
- (UIView *)keyView {
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
if (w.subviews.count > 0) {
return [w.subviews objectAtIndex:0];
} else {
return w;
}
}
I handled it as follows:
- (IBAction)buttonClicked:(UIBarButtonItem *)sender event:(UIEvent *)event
{
UIView* view = [sender valueForKey:#"view"]; //use KVO to return the view
CGRect rect = [view convertRect:view.bounds toView:self.view];
//do stuff with the rect
}

How to flash a custom NSMenuItem view after selection?

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
}

Custom NSScroller issues

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.

Resources