I'm presenting a view in an NSPopover, using code based on this sample code.
The view, and all of its sub-views, are layer-backed. There's a single NSImageView, and several non-editable NSTextFields. The text fields backgroundColors are set to [NSColor textBackgroundColor], and their textColors to [NSColor textColor]. In this way, the text is black if one is using the normal theme, and white if one is using the "dark menu bar and Dock" option (which I'll refer to as "dark theme" from now on). This all works fine, and it looks a little somethin' like this:
Light theme:
Dark theme:
The problem comes when I animate the NSImageView up off the view. As it intersects with the NSTextFields, the image appears to blend with the text fields in an unappealing manner. It happens in both light and dark themes, but it's more icky-looking (it's a technical term) in the dark theme. Dig it:
The code to animate it looks basically like this:
CABasicAnimation* positionAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
positionAnimation.fromValue = [NSValue valueWithPoint:fromPoint];
positionAnimation.toValue = [NSValue valueWithPoint:toPoint];
positionAnimation.duration = imageAnimationDuration;
[self.imageView.layer addAnimation:positionAnimation forKey:#"position"];
self.imageView.layer.position = toPoint;
What have I tried? Oh, what haven't I tried?
First off, my own views don't have any kind of NSVisualEffectView going on. But it seems that NSPopover adds that on its own; you can clearly see my desktop bleeding through the popover in the animation above. That's fine; it's actually a nice effect. But, thinking that my NSImageView was trying to be vibrant, I subclassed NSImageView just to return NO from allowsVibrancy. No change in behavior.
Next, I subclassed NSView to return NO from allowsVibrancy, and made the parent view of my view an instance of that. No change in behavior.
My NSTextFields are set with drawsBackground = NO, so I changed them to YES. No change in behavior. Then, leaving drawsBackground = YES, I set both text field's backgroundColors to [NSColor clearColor]. Here's where it gets weird. This does make the weird drawing go away, but it changes the text color of one of the text fields (the smaller one) to black. Wut? See below.
I gave up on the background colors, and started messing with the text colors. I found that if I set the textColor of the text fields to a discrete color (say, [NSColor blackColor] or [NSColor whiteColor], then the weird drawing problem also goes away. It seems only to get weird when using colors which adapt with the theme such as [NSColor textColor]. That's super lame, because the whole point of using something like [NSColor textColor] is that it adapts to the theme. I could probably hack around and figure out what theme is active and set the colors manually, but I really don't want to go that route if I can help it.
I promise there's a question in here somewhere, and, mercifully, here it is:
How can I fix the animation issue shown above, while still using colors which properly adapt to the current theme?
Sample project on GitHub.
Edit:
The desired result is to have no blending between the image and the text. Something like this:
The image I used in the sample app here maybe isn't the best example to convey the sheer yuckiness of the animation I'm seeing in my actual app. The image in the sample is already mostly white, while in my actual app it's mostly black, and it truly looks horrible when blended with white text.
Visual Effect Views (like used in NSPopover) are totally messed up in OS X Yosemite and they are causing your problem here. It is not your fault, these views are totally buggy.
A workaround on Yosemite should be to set the appearance property of each NSTextField to NSAppearanceNameAqua. Because if the labels don't try to do some weird vibrancy effect, they can't mess things up. The labels still look the same and the strange effect is gone.
My words in code:
self.titleLabel.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
self.descriptionLabel.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
Luckily NSVisualEffectView's are working fine now in El Capitan. So your code should work fine there..
Related
I've recently come across a problem with a custom NSButtonCell subclass. We have a dark interface with white text, and all is going as expected, until we try to make a default button (i.e. assigning a key equivalent of \r). What we get seems mysterious: The bezel draws, but the text doesn't. But if I make the text black, it draws. If I make the text white with a black shadow, only the shadow draws!
The mystery started to unravel when I tried a test with the text set to [NSColor redColor] on a lark. (The "Create" button here has a key equivalent of \r, "Cancel" is Esc.)
What I eventually figured out is that the default button's cell is drawing into a bitmap context, presumably so that the rendered glyphs can be cached for the animated blue pulsing background that the Aqua look gives. That makes total sense, but the surprise is that that bitmap is then drawn with a multiply compositing operation. Hence black shows up and white doesn't, and anything in between shows up darkened.
I've managed to hack a workaround, by taking the text rendering out of -drawTitle:withFrame:inView: and doing it in -drawBezelInFrame:inView: instead. But that gets called repeatedly, so I'll need to manually cache the image. Plus it's just conceptually ugly, because it's specifically the wrong method for rendering the title!
My question: Surely there must be a better way, right? Is there some way to tell the button cell not to automatically use this offscreen rendering path even though the button is default?
I'm using IcoMoon to create a custom font that I use to generate Icons. This however plays no role as the same phenomenon occurs using Helvetica.
Why do 2 things occur on titleLabel of a UIButton on iOS 7?
The right edge of the label is cut off (the button frame is much wider, but the label frame is getting cut off
there is a dark ring around the light gray image.
This looks absolutely horrible and it's basically unacceptable. We'll have to render graphics ourselves and localize images where appropriate.
I would be grateful for any info. The only thing that distinguishes these two checkmarks is that they have different colors set for titleColor
Try setBackgroundImage: instead of setImage:
I have a UIDatePicker in my view and have set the background color of the UIDatePicker:
self.datePicker.backgroundColor = [UIColor lightTextColor];
self.datePicker.layer.cornerRadius = 10;
This successfully puts a background behind the UIDatePicker (which in iOS7 is essentially transparent) but fails to make the rounded corners for the background that I am looking for (I do this same thing for an image on the screen and it works perfectly).
It seems that the corner radius doesn't affect the background color.
Is there a way to fix this problem by setting a corner radius for the background color (or any another solution).
The reason I want to do this is because the ordinary UIDatePicker looks awkward in the view I have constructed and looks much better with a background color.
However, all the other items in the view have rounded corners and I want the UIDatePicker to match them.
Thanks.
You have to add layer.masksToBounds=YES;
Try this,
self.datePicker.backgroundColor = [UIColor lightTextColor];
self.datePicker.layer.cornerRadius = 10;
self.datePicker.layer.masksToBounds=YES;
UIBuilder Swift 5 solution:
See picture, it's the same as #ToseefKhiji's solution but even easier to execute for fans of UIBuilder controls.
This is how it comes out (I have added other features to the picker programmatically, such as borderWidth, color, etc...)
UIBuilder is underappreciated by Swift/iOS coders as everyone has rushed to SwiftUI as the new new thing.
I am developing a iOS-6 app. I have a UIViewController with a view that needs fixed orientation (portrait mode). But when the phone is rotated, one control on that view needs to be moved and rotated (so that it will always be in the upper left corner, and its text will be readable).
I am achieving this by shifting the control(a UIView) using the frame-property of my control (it is a custom view, more on that later), and then using CGAffineTRansformMakeRotate() afterwards, since I know that it's not advisable to use the frame after rotating a view. Everything is fine so far, but here's the thing: That custom view has three UIButtons of type UIButtonTypeCustom as its subviews. Because I rotated the View, but cannot rotate the buttons inside the view (they are not squares), I need to rotate the titleLabels of the Buttons for the text to be readable in the new deviceOrientation.
But it won't work very well. The text will be rotated, as I intended, but it will be clipped by the titleLabel, because the titleLabel has the wrong frame. I checked this by applying borders to the label. So I need to change the titleLabels frame, right? But how can I do that? I tried setting it using [titleLabel setFrame: frameThatFits];, but to no avail. (frameThatFits is a CGRect I created). Also, calling [button.titleLabel sizeToFit]; has no effect that I could see.
I am using [button setTitle:title forControlState: UIControlStateNormal];to set the title.
TL;DR: I'm trying to change the frame/bounds of a UIButtons titleLabel after rotating it using an affine transformation. Any help?
Thanks.
PS: I can supply code when needed, but I wouldn't know what to show you. Tell me what you need, I'll post it.
OK, first of all, thanks to everyone who tried to help. Im posting an alternative solution for my problem, and although it doesnt really address the problem of changing the titleLabels dimensions, it will result in the proper display of my ViewController.
It turns out using the frame is a bad idea. I initially used the frame to reposition the view and i figured that this couldnt be a problem because i only ever applied transformations afterwards, but i was wrong. Because OBVIOUSLY i tried to change the titleLabels frame. AFTER the rotation. And that didnt work.
So the way to go here is using the center-property and the bouds of the view consistently throughout the code. It will result in properly rotated Buttons, that do not need any fidgeting afterwards.
My takeaway here is that i will never ever again use the frame-property outside of a NSLog-statement. But why [button sizeToFit];wouldnt yield any results is still beyond me. If i ever figure it out, i might post it if i remember.
EDIT:
#ZevEisenberg nailed it with this comment:
“Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.” So you are right to use the center and bounds here, but if you do not have a transform, the frame is perfectly safe to use.
NEXT EDIT:
Heres how i ended up repositioning the Buttons:
-(CGPoint)centerForView:(UIView *)view{
//calculate a suitableposition for the view
//depending on the current orientation and the device type (iphone 4S/5, etc)
return point;
}
Then, as a reaction to the deviceOrientation change notification, i apply CGAffineTransformIdentity to all the views, reposition them using my centerForView shown above, and apply the correct rotation transformation to the View. I do this for all the subviews every time the divice rotates, like so:
-(void)setRightRotationTransformations{
[self resetAllTransformations];
self.someSubview.transform = CGAffineTransformRotate(self.someSubview.transform, -M_PI_2);
}
In my case works such hack:
set Line Break mode to Word Wrap
Add extra line to title (even for one line title)
It seems Apple's UIToolbar and some of the controls you can place on it, such as UISegmentedControl, allow very limited customization. For example, we want our toolbar to looks something like this mock:
However, UIToolbar only has a single tintColor and 3 built-in barStyle values that you can apply to it, so we can't get the gradient we're after. Is there some way to set the background image like many of the other controls?
Even worse, the UISegmentedControl doesn't let you specify font or font color. It seems to enforce white 11pt Helvetica with a drop shadow, and the white is almost unreadable with the light tintColor we're using.
I'd hate to rewrite UISegmentedControl just to change the text color. But the only solution I've seen is very hacky -- it digs into the subviews of the subviews and changes properties on those -- seems likely to stop working whenever Apple feels like changing or disallowing it.
Is Apple really against us changing the text color on our segmented controls, or is there something I'm missing here?
Update: Apple finally added functions for customizing appearance in iOS 5. See jfortmann's answer.
You want to use the setBackgroundImage:forState:barMetrics: methods (probably on your appearance proxy) to set the background image. The text can be modified with setTitleTextAttributes:forState:. And finally, you'll need to change the divider for the possible states with setDividerImage:...
The docs are very informative as to how the images have to look.