Using NSBackgroundStyleLowered on an NSButtonCell? - objective-c

I use this snippet of code...
[[textField cell] setBackgroundStyle:NSBackgroundStyleLowered];
...to give a piece of text a shadow, and it works. When I try to do the same thing with a button:
[[refreshButton cell] setBackgroundStyle:NSBackgroundStyleLowered];
the code doesn't work. The button is a Momentary Change button with a white transparent circular arrow. Any ideas why this couldn't be working? It seems like it would work, since it is still a cell.

NSCell subclasses have different drawing behaviors. So a settable background style doesn't mean that the style is actually used in the concrete subclass.
NSButtonCells use the interiorBackgroundStyle property before drawing the title. This property doesn't expose a setter, so you'd have to subclass NSButtonCell and set the cell class in Interface Builder accordingly.
To achieve the lowered background style, override interiorBackgroundStyle in your subclass:
- (NSBackgroundStyle)interiorBackgroundStyle
{
return NSBackgroundStyleLowered;
}
If you need more control over the drawing, you could also override NSButtonCell's drawInteriorWithFrame:inView:.
A hacky approach (that doesn't require subclassing) would be to modify the attributed title string to achieve a similar effect:
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowOffset:NSMakeSize(0,-1)];
[shadow setShadowColor:[NSColor whiteColor]];
[shadow setShadowBlurRadius:0];
NSAttributedString* title = [button.cell attributedTitle];
NSMutableDictionary* attributes = [[title attributesAtIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, title.length)] mutableCopy];
[attributes setObject:shadow forKey:NSShadowAttributeName];
NSAttributedString* string = [[NSAttributedString alloc] initWithString:[button.cell title] attributes:attributes];
[button.cell setAttributedTitle:string];

Related

UIAppearance has no effect on Label text color

I am setting the text color of all the labels in my app using UIAppearance. Yet the text color does not change.
Here is a sample of how i create the label
//show the loading message
MessageLabel *messageLabel = [[MessageLabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
messageLabel.text = #"\n\nLoading ...\n\n";
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = NSTextAlignmentCenter;
[messageLabel sizeToFit];
self.tableview.backgroundView = messageLabel;
Here is how i set the text color
[[MessageLabel appearance] setTextColor:[UIColor blackColor]];
One note is that all these MessageLabel are BackgroundViews of UITableView
As arrteme mentioned, UILabel's textColor doesn't work with UIAppearance. I got around this by adding a UIAppearance compatible property that just wraps textColor. In Swift this would look something like
class MessageLabel: UILabel {
#objc dynamic var configurableTextColor: UIColor {
get {
return textColor
}
set {
textColor = newValue
}
}
...
}
and then to configure the appearance:
MessageLabel.appearance().configurableTextColor = .black
From the Documentation:
iOS applies appearance changes when a view enters a window, it
doesn’t change the appearance of a view that’s already in a window. To
change the appearance of a view that’s currently in a window, remove
the view from the view hierarchy and then put it back.
Referring to this, UIAppearance kind of doesn't really seem work with UILabel...
Since you're subclassing from UILabel, maybe it would make sense to set textcolor property initWithFrame: method on in your MessageLabel class?
Or another option, since you say these MessageLabel instances are used for UITableViewCell's background, maybe it would make sense to leave label's background clear and change background of cell itself, for example in tableView:willDisplayCell:forRowAtIndexPath: method in tableView's delegate?
If you instantiated a UILabel in code, you must manually set textColor property after you set the appearance.
messageLabel.textColor = [[UILabel appearance] textColor];

How do you customise UISearchBar colors in iOS7?

I have searched quite a bit but cannot find a good answer to this.
I want to change the backgroundColor of the inner rounded view.
Like in Tweetbot on the search tap where it changes from gray to blue.
I understand that I probably need to iterate over the subviews but I don't know how to get the right object. (for the backgroundColor it's not the _searchLabel)
The default contrast of this element is so bad it's not even funny :(
Ok, this works. But note that you can't set a UIBarStyle beforehand or it will override everything.
https://stackoverflow.com/a/19836215/1252720
If you're still looking for a better answer, I just stumbled across this thread and found a great solution: UISearchBar text color change in iOS 7
If you look at the answer given by Sandeep-Systematix (not the accepted answer, but the answer right below), he mentions a really clean way to modify subviews in any class with this method:
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:[UIColor blueColor]];
You can read more about this in Apple's documentation: https://developer.apple.com/library/ios/documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html
That said, here's what you'll need to change the white, rounded background of the UITextField inside the UISearchBar:
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:[UIColor redColor]];
Now if you needed to create different UISearchBars with different styles, you would simply create a subclass of UISearchBar and you'd end up with something like this:
[[UITextField appearanceWhenContainedIn:[MyCustomSearchBar class], nil] setBackgroundColor:[UIColor redColor]];
Use this code.
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.contentView.bounds.size.width, 44.0f)];
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_searchBar.delegate = self;
[self.view addSubView:_searchBar];
UITextField *txfSearchField = [_searchBar valueForKey:#"_searchField"];
txfSearchField.backgroundColor = [UIColor redColor];

Why doesn't this code draw white text?

This code doesn't draw white text, why?
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
[style setAlignment:NSCenterTextAlignment];
NSFont *font = [NSFont fontWithName:#"System" size:13];
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:style, NSParagraphStyleAttributeName, font, NSFontAttributeName, [NSColor whiteColor], NSForegroundColorAttributeName, nil];
[button.title drawInRect:textRect withAttributes:attrs];
(Assuming the [cocoa] tag doesn't mean cocoa touch)
It's because NSButton is likely overriding the choice you've made to draw it's text in when it's -drawRect: gets called again. You can apply the attributes you've given in that dictionary to an NSAttributedString and call setAttributedTitle: to keep your style choices around.
If you need more fine-grain control over text rendering, either edit and move your logic into -drawRect: if it isn't already there, or provide an NSTextField or NSTextView as appropriate.
The main problem with the code you've provided is that #"System" isn't a font name.

Mac OS X: How to force a Field Editor to scroll instead of wrap text?

I have a Cocoa, Document-based Mac OS X application.
One feature that I have in my app is a list of text items which can be double-clicked to edit. When the user double-clicks one of the text items, I place the current window's fieldEditor text field over the clicked text item to allow editing.
Everything is working fine except for one problem. I cannot figure out how to make the fieldEditor text field clip + scroll rather than wrap its text. Here's what it currently looks like:
See how the text is wrapping to a second line? I don't want that. I'd like it to remain one line which scrolls (and appears clipped).
Here's an example of it working correctly on a list item which has less text:
Here's what I'm trying in my view controller:
NSWindow *win = [listItemView window];
NSText *fieldEditor = [win fieldEditor:YES forObject:listItemView];
[fieldEditor setFont:[TDListItemView titleFont]];
[fieldEditor setAlignment:NSLeftTextAlignment];
[fieldEditor setDrawsBackground:YES];
[fieldEditor setBackgroundColor:[NSColor whiteColor]];
[fieldEditor setString:str];
[fieldEditor setDelegate:self];
[fieldEditor selectAll:nil];
if ([fieldEditor isKindOfClass:[NSTextView class]]) {
NSTextView *tv = (NSTextView *)fieldEditor;
NSMutableParagraphStyle *style = [[[tv defaultParagraphStyle] mutableCopy] autorelease];
[style setLineBreakMode:NSLineBreakByClipping];
[tv setDefaultParagraphStyle:style];
}
CGRect r = [self fieldEditorRectForBounds:[listItemView bounds] index:idx]; // height here is 10.0
[fieldEditor setFrame:r];
[fieldEditor setNeedsDisplay:YES];
[[self view] addSubview:fieldEditor];
[win makeFirstResponder:fieldEditor];
Notice that part in the middle: I check to see if this fieldEditor is an instance of NSTextView in order to call the setDefaultParagraphStyle: method on it. This is my attempt to get the fieldEditor to clip its text -- via the NSLineBreakByClipping value. It's not having any effect. And I'm not even sure this is what I should be doing to get the fieldEditor to scroll on one line.
Also note that the height of the rect which I compute in my -fieldEditorRectForBounds:index: method is correct, and is providing a rect which is correctly sized for a single line of text (14.0 pixels in this case).
What am I missing to make the fieldEditor display a single line of scrolled/clipped text?
I've also tried adding these lines in the middle section:
[[tv textContainer] setHeightTracksTextView:YES];
[[tv textContainer] setWidthTracksTextView:YES];
This has the desired effect of resizing the visible portion of the fieldEditor which is good. But the bad news is that it doesn't change the fact that the text is still wrapped, rather than clipped + scrolled. :(
This seems related to this constant:
NSStringDrawingUsesLineFragmentOrigin
which can be used in the options: argument of:
-[NSAttributedString drawWithRect:options:attributes:]
but in my case, I'm working from a NSText field editor, not an NSAttributedString. I can't figure out how to do this sort of thing with an NSText field editor.
Hmmm ... NSTextView relies on an NSScrollView entirely for its scrolling behavior. I'm not sure if there's more to this that I'm not seeing, but it looks like the "historic" solution to your problem is either to:
Use A Different Control
You can use an editable NSTextField in its standard IB label configuration with editing enabled and scrolling selected as the behavior. It does all the heavy lifting for you already when configured properly - no need to mess around with the field editor directly.
...or to...
Cheat
Slap the field editor into an appropriately-sized and configured NSScrollView (allow only horizontal scroll; don't show the scrollers) dynamically, then remove the scroll view when finished editing.
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
[style setLineBreakMode:NSLineBreakByTruncatingHead];
NSMutableDictionary* attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:style forKey:NSParagraphStyleAttributeName];
NSTextView* textView = [[NSTextView alloc] init];
[textView setTypingAttributes:attributes];
If you use NSTextView, please try below codes ....
[self.textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
[self.textView setHorizontallyResizable:YES];
[[self.textView textContainer] setWidthTracksTextView:NO];
[[self.textView textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];

Draw NSShadow inside a NSView

I'm trying to draw a NSShadow on the background of a NSView. I want to use it as a replacement for NSGradient, as I need to support Mac OS X Tiger. How may I do that? I know this must be pretty easy and I must be making some mistake.
Thanks!
The easiest approach may be to just set the shadow properties for the view's layer. If you have a NSView* named view, it'd be something like:
[[view layer] setShadowOpacity:0.5];
Setting the shadow opacity to something greater than 0 will make the shadow visible. The shadow drawn will be similar to the view's alpha channel, so whatever you draw in the view will have a shadow. There are several other shadow attributes that you can set, such as the blur radius. Take a look at the CALayer reference page for more.
If you must use NSShadow, then just set up a shadow before you do your drawing:
- (void)drawRect:(NSRect)rect
{
NSShadow *shadow = [[[NSShadow alloc] init] autorelease];
[shadow setShadowBlurRadius:3.0];
[shadow setShadowOffset:NSMakeSize(0.0, 5.0)];
[shadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.6]];
[shadow set];
// continue with your drawing...
}