I've a NSPopupButton and I want it so resize itself to fit the selected title.
[NSPopupButton sizeToFit] doesn't fit my needs because the popup is resized to the largest title item not to the current selected one
I've tried in may ways without success the closer is
#define ARROW_WIDTH 20
NSDictionary *displayAttributes = [NSDictionary dictionaryWithObjectsAndKeys:[popup font], NSFontAttributeName, nil];
NSSize titleSize = [popup.titleOfSelectedItem sizeWithAttributes:displayAttributes] + ARROW_WIDTH;
But the constant value ARROW_WIDTH is a really dirty and error prone solution.
TextWrangler encoding combo on status bar works like I need
The way I've handled these problems with text fields is to try the resizing with a text field that you never add to the view hierarchy. You call sizeToFit on an object that you have no intention of reusing, then use that to figure out how wide your actual control needs to be to fit what you need to do.
So, in pseudo code, you'd do this (assuming you're using ARC, YMMV for non-ARC projects as this will leak):
NSArray *popupTitle = [NSArray arrayWithObject: title];
NSPopUpButton *invisiblePopup = [[NSPopUpButton alloc] initWithFrame: CGRectZero pullsDown: YES];
// Note that you may have to set the pullsDown bool to whatever it is with your actual popup button.
[invisiblePopup addItemWithTitle: #"selected title here"];
[invisiblePopup sizeToFit];
CGRect requiredFrame = [invisiblePopup frame];
self.actualPopup.frame = requiredFrame;
For projects with autolayout override method intrinsicContentSize in subclass of NSPopUpButton
class NNPopUpButton: NSPopUpButton {
override var intrinsicContentSize: NSSize {
let fakePopUpButton = NSPopUpButton(frame: NSZeroRect, pullsDown: false)
fakePopUpButton.addItem(withTitle: title)
fakePopUpButton.sizeToFit()
var requiredFrame = fakePopUpButton.frame
requiredFrame.size.width -= 35 // reserved space for key equivalent
return requiredFrame.size
}
}
Related
This answer describes how to set the font, and thus the font color, of an NSMenuItem.
In order to alert the user to a problem with the selected item in a popup menu, I set the color to red. Works great, except when the item is highlighted, the background becomes blue, and my red-on-blue is hard to read and looks lousy. The font of regular menu items changes from black to white. I would like my modified menu item to change its font color when highlighted like that.
This is a dynamic menu. I set the font/color when items are created, in -menuNeedsUpdate. Of course, -[NSMenuItem isHighlighted] returns NO there because the item has just been created.
I also tried adding an observer on NSMenuDidBeginTrackingNotification and NSMenuDidBeginTrackingNotification, but that doesn't help either because these two notifications are always received in pairs, three to six pair each time I click the menu, and then after tracking has ended comes another -menuNeedsUpdate: which re-creates everything from scratch again. I'm not sure what it means when a menu is "tracking", but apparently it's not what I want.
I thought I'd ask if anyone has ever come up with a good answer for this, before I go off and do something really kludgey like these guys did for a similar NSMenuItem quandary.
You can implement the menu's delegate to be notified when an item is highlighted.
#pragma mark - NSMenuDelegate
- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item {
[menu.highlightedItem nik_restoreTextColor];
[item nik_overrideTextColor:[NSColor selectedMenuItemTextColor]];
}
It should be pretty straightforward to remove and re-add the color of a single item.
But here's the generic solution I'm using to remember and later restore the color:
#implementation NSMutableAttributedString(NIKExchangeAttribute)
- (void)nik_renameAttribute:(NSString *)originalAttribute to:(NSString *)newAttribute {
NSRange fullRange = NSMakeRange(0, self.length);
[self removeAttribute:newAttribute range:fullRange];
[self enumerateAttribute:originalAttribute
inRange:fullRange
options:0
usingBlock:^(id value, NSRange range, BOOL *stop) {
[self addAttribute:newAttribute value:value range:range];
}];
[self removeAttribute:originalAttribute range:fullRange];
}
#end
static NSString *const ORIGINAL_COLOR_KEY = #"nik_originalColor";
#implementation NSMenuItem(NIKOverrideColor)
- (void)nik_overrideTextColor:(NSColor *)textColor {
NSMutableAttributedString *title = [self.attributedTitle mutableCopy];
[title nik_renameAttribute:NSForegroundColorAttributeName to:ORIGINAL_COLOR_KEY];
[title addAttribute:NSForegroundColorAttributeName
value:textColor
range:NSMakeRange(0, title.length)];
self.attributedTitle = title;
}
- (void)nik_restoreTextColor {
NSMutableAttributedString *title = [self.attributedTitle mutableCopy];
[title nik_renameAttribute:ORIGINAL_COLOR_KEY to:NSForegroundColorAttributeName];
self.attributedTitle = title;
}
#end
I am developing my first MAC application, i downloaded one Example of PxListView
and i have to added one button and background image on cell xib and bind them with controller
and, when on button click i was set height of that cell is much bigger then other. that is done,
and work fine.
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example?
pls help me to give some suggest how it will be done.
for Ex like before click on button
after chick on button i want to develop like
You write
i have to added one button and background image on cell xib and bind them with controller
It sounds like you've subclassed PXListViewCell--for convenience, let's call your subclass TemplateListViewCell--and added a xib from which instances of TemplateListViewCell will be loaded in
+[PXListViewCell cellLoadedFromNibNamed:bundle:reusableIdentifier:]
In addition, there is a[t least one] button in TemplateListViewCell.xib.
You write
when on button click i was set height of that cell is much bigger then other. that is done, and work fine
It sounds like this button has as its action a method on TemplateListViewCell such as
- (IBAction)toggleDetail:(id)sender
{
//Code to grow or shrink the height of [self frame].
//...
}
In my approach to implementing -toggleDetail, two modifications to the PXListView files were necessary:
1. Adding a protocol method
- (void)listView:(PXListView *)aListView setHeight:(CGFloat)height ofRow:(NSUInteger)row;
to the PXListViewDelegate protocol.
2. Adding a property
#property (nonatomic, assign) BOOL expanded;
to PXListViewCell.
My implementation of -toggleDetail looks something like this:
- (IBAction)toggleDetail:(id)sender
{
BOOL wasExpanded = [self expanded];
NSRect oldFrame = [self frame];
CGFloat oldHeight = oldFrame.size.height;
CGFloat newHeight = oldHeight;
CGFloat heightIncrement = 0.0f;
if (wasExpanded) {
heightIncrement = -80.0f; //use whatever value is appropriate
} else {
heightIncrement = 80.0f; //use whatever value is appropriate
}
newHeight += heightIncrement;
[[[self listView] delegate] listView:[self listView] setHeight:newHeight ofRow:[self row]];
[[self listView] reloadData];
BOOL isExpanded = !wasExpanded;
[self setExpanded:isExpanded];
}
It might seem better to use [[self listView] reloadRowAtIndex:[self row]]; in place of [[self listView] reloadData], but unfortunately, this doesn't work: if the user hides the detail--shrinks the cell vertically--new cells which should appear on the screen do not.
You write
that is done, and work fine.
It sounds like you were able to implement successfully a method analogous to -[TemplateListViewCell toggleDetail:].
You write
but now i want to develop like after is witch cell has open in that cell i want to add some extra contain (Controller) on it, so how it will possible using given example? pls help me to give some suggest how it will be done.
It sounds like you want instances of TemplateListViewCell to contain extra views if they are expanded.
It might seem tempting to put this code into -[TemplateListViewCell toggleDetail], but this will not work out as we might hope. The trouble is, we need to handle cases where expanded cells have been scrolled out of view and scrolled back into view.
To get this right, we need to have a notion of expanded which persists beyond the usage of a PXListViewCell subclass instance: we either need to keep track of expansion in the PXListView itself or in its delegate.
The better--but less expedient--design seems to be to keep track of this information in the PXListView itself. For the sake of this question, however, I'll demonstrate how to keep track of cell expansion in the delegate. To do this, I'm expanding the PXListViewDelegate protocol and making other changes to the PXListView files:
1. Adding the methods
- (void)listView:(PXListView *)aListView setExpanded:(BOOL)expanded atRow:(NSUInteger)row;
- (BOOL)listView:(PXListView *)aListView expandedAtRow:(NSUInteger)row;
to PXListViewDelegate.
2. Adding the method
- (void)setCell:(PXListViewCell *)cell expandedAtRow:(NSUInteger)row
{
if ([[self delegate] respondsToSelector:#selector(listView:expandedAtRow:)]) {
[cell setExpanded:[[self delegate] listView:self expandedAtRow:row]];
}
}
to PXListView.
3. Calling -[PXListView setCell:expandedAtRow:] from -[PXListView layoutCells]
- (void)layoutCells
{
//Set the frames of the cells
for(id cell in _visibleCells)
{
NSInteger row = [cell row];
[cell setFrame:[self rectOfRow:row]];
[self setCell:cell expandedAtRow:row];
[cell layoutSubviews];
}
NSRect bounds = [self bounds];
CGFloat documentHeight = _totalHeight>NSHeight(bounds)?_totalHeight:(NSHeight(bounds) -2);
//Set the new height of the document view
[[self documentView] setFrame:NSMakeRect(0.0f, 0.0f, NSWidth([self contentViewRect]), documentHeight)];
}
and from -[PXListView layoutCell:atRow:]:
- (void)layoutCell:(PXListViewCell*)cell atRow:(NSUInteger)row
{
[[self documentView] addSubview:cell];
[cell setFrame:[self rectOfRow:row]];
[cell setListView:self];
[cell setRow:row];
[cell setHidden:NO];
[self setCell:cell expandedAtRow:row];
}
4. Setting _expanded to NO in -[PXListViewCell prepareForReuse]:
- (void)prepareForReuse
{
_dropHighlight = PXListViewDropNowhere;
_expanded = NO;
}
Note: In the sample PXListViewCell subclass, MyListViewCell, distributed with PXListView, the implementation of -[MyListViewCell prepareForReuse] fails to call [super prepareForReuse]. Make sure that this call is made in [TemplateListViewCell prepareForReuse]:
- (void)prepareForReuse
{
//...
[super prepareForReuse];
}
One change needs to be made to -[TemplateListViewCell toggleDetail:]. The line
[self setExpanded:isExpanded];
needs to be replaced by
[[[self listView] delegate] listView:[self listView] setExpanded:isExpanded atRow:[self row]];
Once you've set up your PXListView's delegate to properly handle the new delegate methods, you're ready to override [PXListViewCell setExpanded:] in your subclass TemplateListViewCell:
- (void)setExpanded:(BOOL)expanded
{
if (expanded) {
//add detail subviews
} else {
//remove detail subviews
}
[super setExpanded:expanded];
}
Replace //add detail subviews with your own code which programmatically adds the detail subviews that you want and replace //remove detail subviews with code to remove the detail subviews that you want, checking to see that they are present first.
You write
i want to add some extra contain (Controller) on it
It sounds like you want to add view controllers rather than views to your TemplateListViewCell. To do this, use an NSBox and set the box's contentView to your view controller's view. (For details on this, see this answer.)
If you plan on just showing a single view controller's view in an NSBox on the expanded TemplateListViewCell, you can just (1) add a property to TemplateListViewCell referencing your view controller and (2) add an NSBox to TemplateListViewCell xib and set its contentView to the appropriate view controller's view on [cell setExpanded:YES] and set its contentView to nil on [cell setExpanded:NO].
I have a series of UIImageViews which are created in IB. I want to show and hide these depending on some button presses. I currently have a method which says if 1 is pressed then hide 2,3,4 then if 2 is pressed then hide 1,3,4. This works but I am trying to improve my code for an update.
My background is actionscript so I'm not sure if what I am trying to do is the right thing.
I basically want to evaluate a string reference to UIImageView, in AS I would use eval(string).
The method I am using is to create a string from a string and a number, so I get "image1". All works, but then I need to evaluate this into a UIImageView so I can update the alpha value.
Firstly is this possible and if not how should I be doing this? I'm starting to think that having this set up in interface builder maybe isn't helping?
That's probably not a good way to work. What you want is an array of imageViews. Then you just need a numeric index, and you can go through the array of imageViews hiding everything that doesn't have the chosen index.
But how do you get an array of imageViews? See How to make IBOutlets out of an array of objects? It explains how to use IBOutletCollection.
If you have a separate button for each view, put those into an IBOutletCollection too. That way you can have something like this:
- (IBAction) imageButtonPressed:(id) sender;
{
// The sender is the button that was just pressed.
NSUInteger chosenIndex = [[self imageButtons] objectAtIndex:sender];
for (NSUInteger imageIndex = 0; imageIndex < [[self imageViews] count]; imageIndex++)
{
// Hide all views other than the one associated with the pressed button.
if (imageIndex != chosenIndex)
{
[[[self imageViews] objectAtIndex:imageIndex] setHidden:YES];
}
else
{
[[[self imageViews] objectAtIndex:imageIndex] setHidden:NO];
}
}
}
If you really, really need to associate the string image1 with an imageView, you can construct an NSDictionary associating your controls with unique string identifiers for later lookup. NSDictionary is powerful, but I'm drawing a blank on reasons why this would be needed.
NSMutableDictionary *viewLookup;
[viewLookup setObject:[[self imageViews] objectAtIndex:0] forKey:#"image0"];
[viewLookup setObject:[[self imageViews] objectAtIndex:1] forKey:#"image1"];
[viewLookup setObject:[[self imageViews] objectAtIndex:2] forKey:#"image2"];
[viewLookup setObject:[[self imageButtons] objectAtIndex:0] forKey:#"button0"];
// ...
// Can now look up views by name.
// ...
NSString *viewName = #"image1";
UIView *viewFound = [viewLookup objectForKey:viewName];
[viewFound doSomething];
I have created a view that contains a CGGradient:
// Bar ContextRef
CGRect bar = CGRectMake(0, screenHeight-staffAlignment, screenWidth, barWidth);
CGContextRef barContext = UIGraphicsGetCurrentContext();
CGContextSaveGState(barContext);
CGContextClipToRect(barContext,bar);
// Bar GradientRef
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[16] = { 1.0,1.0,1.0,0.0, 0.0,0.0,0.0,1.0, 0.0,0.0,0.0,1.0, 1.0,1.0,1.0,0.0};
CGFloat locations[4] = {0.95,0.85,0.15,0.05};
size_t count = 4;
CGGradientRef gradientRef = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
// Draw Bar
CGPoint startPoint = {0.0,0.0};
CGPoint endPoint = {screenWidth,0.0};
CGContextDrawLinearGradient(barContext, gradientRef, startPoint, endPoint, 0);
CGContextRestoreGState(barContext);
This code is called in the drawRect method of the UIView. I then use a UIViewController to access the created view.
- (void)loadView {
MainPageView *mpView = [[MainPageView alloc] initWithFrame:[window bounds]];
[self setView:mpView];
[mpView release];
}
and displayed on the screen through the appDelegate:
mpViewController = [[MainPageViewController alloc] init];
[window addSubview:[mpViewController view]];
[window makeKeyAndVisible];
The UIView contains more objects, such as UIButtons, that are visible. I am assuming because they are added as a subview. But I can't work out how to add the CGGradient as a subview? Does it need to be? Is there another reason CGGradient is not visible?
I also don't get the functionality on the UIButtons. I guess that is because of where I have added the UIButtons to the view. Do the buttons need to be added in the UIViewController or the appDelegate to have functionality. Sorry to ask what would seem like simple questions but I am trying to accomplish the programming without the Interface Builder and material on that is scarce. If anyone could point me in the right direction on both these problems I would really appreciate it.
Thanks!
The functionality on the buttons was lost because the frame was too large but the buttons were still visible because the background was clearColor
so i have a quick question i have the method below which sets the alpha value of a window depending on the value from a slider, however the content of the window also becomes translucent and eventually disappears with the window.
Is there a way to just change the alpha value of the window and not the content view inside it?
- (IBAction)changeTransparency:(id)sender {
// Set the window's alpha value. This will cause the views in the window to redraw.
[self.window setAlphaValue:[sender floatValue]];}
Thanks, Sami.
Apple's docs gives a way to do this. The key is to set the window's backgroundColor's alpha to the desired value. You must also make sure to set the window's opaque property to NO (which is YES by default.)
e.x.
// At some point in your code...
[window setOpaque:NO];
// Then in your changeTransparency: method...
NSColor *backgroundColor = [window backgroundColor];
backgroundColor = [backgroundColor colorWithAlphaComponent:[sender floatValue]];
[window setBackgroundColor:backgroundColor];
Here is another way.
Suppose,
self.window <--- base view and this alpha will be changed (but exacatly fake).
subView1, subView2 <-- these views are contents of self.window. and theier alpha should not be changed.
self.window.backgroundColor = [UIColor clearColor];
UIView* anAlphaView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.window.frame.size.widht, self.window.frame.size.height)];
anAlphaView.backgroundColor = [UIColor blackColor]; // as you want
anAlphaView.alpha = 0.5f; // as you want.
[self.window addSubview:anAlphaView];
[anAlphaView release];
[self.window addSubview:subView1]; // you should add sub views to self.window
[self.window addSubview:subView2];
You can make a method using above code :)