How to remove the "fuzzy" drop shadow in the UIPopoverController - ios

I don't want the drop shadow when the UIPopoverController's view appears. Is there a way to remove this drop shadow look?

Not straight forward, but since iOS 5, you can make your own custom popover background using UIPopoverBackgroundView.
See the answer for this question: Using UIPopoverBackgroundView class. It's pointing to a good tuto.
Then, in the initWithFrame of your UIPopoverBackgroundView implementation, you can use a clearColor for the drop shadow. Using offset and radius did not work for me.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.layer.shadowColor = [[UIColor clearColor] CGColor];
}
return self;
}

The shadow is an attribute of the popover view's layer. If you could get access to the layer, you could set it's shadow radius to 0.0 and shadow offset to {0.0, 0.0}. However, it looks like the view must be a private ivar of the popover controller, so there's not an easy way to get to it. Moreover, if you're looking to distribute this app through the app store, using a private ivar and changing the look of standard UI elements both are likely to get your app rejected.

You just have to use your custom UIPopoverBackgroundView and there implement this function:
+ (BOOL)wantsDefaultContentAppearance {
return NO;
}

Related

Getting UIButton width before viewDidAppear, with AutoLayout

I have a UIButton (created in interface builder), that I'm turning into a circle by setting button.layer.borderRadius = button.frame.size.width / 2.0; (programatically, in viewDidAppear:). However, the viewController it belongs to is presented modally with an animation. Since viewDidAppear isn't called until after the transition animation has finished, the button is square until then, which makes the sudden change quite jarring.
I can't set the radius in viewDidLoad, since the button properties are incorrect then (the width is too large), which I think is because autolayout constraints haven't been properly resolved yet. I tried to rectify this by calling [self.view setNeedsLayout] in viewDidLoad, and then setting the cornerRadius, but the button width was still wrong. What I don't understand is, during the animation, everything otherwise renders correctly, suggesting that the autolayout constraints /have/ been resolved, or that iOS does something else in the name of quick animations (like storing a snapshot preview to use for the animation).
Any suggestions?
The result of trying to set the corner radius in viewDidLoad:
You can get the width in the function
- viewDidLayoutSubviews.
Apple Documentation here.
Override the UIButton and make its layoutSubviews method like this:
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.cornerRadius = self.bounds.size.width/2.f;
}
Then whenever the button's size changes it will adjusts its value.
Also add it in buttonWithType: and initWithFrame: as I'm not sure if the layoutSubviews is called after init.
Your controls get the constraints and frame set after viewDidLoad amd after viewDidLoad you can get your requirements in viewwilllayoutsubviews or in viewdidlayoutsubviews before viewDidAppear
what about
-(void) viewWillAppear:(BOOL)animated
Sorry I didn't get you well. As your screenshot explains that your image isn't being circular.For that you can try:(1) layer.cornerRadius = btn.frame.size.width/2; or layer.CornerRadius = 50(if width is 100)
layer.masksToBounds = YES;
layer.borderWidth = 1.5; or whatever you want
layer.borderColor = [UIColor whiteColor];
If still you get problem, then share your constraints that you added on button and also the code where you are doing this.

Do I really need drawRect() in custom UIView?

I am new to iOS and trying to understand the use for drawRect() in custom UIViews, so I have simple custom view which I initialize from code.I want to update its colors for instance and I see two approaches as shown below. Which one should I use and why?
//VController
CustomView *cv = [[CustomView alloc] initWithFrame:...]
...
[cv updateColors];
//CustomView
-(id) initWithFrame {}
-(id) initWithCoder {}
-(void) updateColors(UIColor *color){ ----(1)
...Draw here with new color ...
view1.backgroundColor = color;
view2.backgroundColor = color;
}
- (void) drawRect{
... draw here with new color ... ---------(2)
view1.backgroundColor = color;
view2.backgroundColor = color;
}
If all you want to do is change the background color of this view or some of its subviews, you absolutely should not misuse drawRect: for this. drawRect: is for when you want to draw the view (i.e. its content) when the system believes its needs refreshing; it is called at many and unpredictable times, and you don't need that - you just need to change the background color, a feature of the view, on demand. Similarly drawRect: is not the place to perform management of subviews.
But if you are going to draw the view's content (e.g. the view displays a circle and you need to draw that circle to portray the view) then you must use drawRect: for that; it is the only place where the view gets a chance to draw itself.

Button with border (most optimized)

In my app I need to create a button with a 1px colored border with a corner radius. Width may be different.
Naturally I will create a UIButton setting its layer.cornerRadius and layer.borderWidth.
Is it optimal?
Is there a betterway to achieve this?
Another developer told me that doing this way is expensive.
What do you think?
Thanks.
Subclass UIButton. May be named ViewWithThinBorder .
In its implementation in awakeFromNib method add border corner and width properties.
Now you can go to interface builder select button and set its class to ViewWithThinBorder from identity inspector.
This is not only optimized its more reusable anywhere around the interface builder. Also you can change buttons around all view hierarchy from central location.
#import "ViewWithThinBorder.h"
#implementation ViewWithThinBorder
-(void)awakeFromNib
{
self.layer.borderColor = [UIColor groupTableViewBackgroundColor].CGColor;
self.layer.borderWidth = 1.0f;
self.layer.cornerRadius = 1.0f;
}
#end

Pattern to fill the entire screen

Every screen of my app has a common tint. Its not a background. Its a pattern that fills the entire screen and it is top of all the views. You can see the pattern flow continuously from one view to another inside the same screen. And it neither obscures other elements nor participate in event handling.
I tried implementing it with this code in my ViewController.
UIColor* texture = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Texture.png"]];
UIView* tintView = [[UIView alloc] initWithFrame:self.view.bounds];
[tintView setBackgroundColor:texture];
[tintView setAlpha:0.5];
[self.view addSubview:tintView];
But it doesn't pass on touches to the views behind it.
tintView shouldn't participate in any event handling. Rather it should let other elements behind it, handle the events like they do it normally.
Other way of doing it is set this as a background of the view property of a UIViewController and set a common alpha for all other subviews of view to show the pattern behind. That will be redundant in most ways.
Any better way of doing this?
Make your tintView a subclass of UIView and implement the hitTest:withEvent: method, returning nil. This will make your view transparent to touches. Or set userInteractionEnabled to NO.
Set the background color with a Textured image
UIImage *bgimg = [UIImage imageNamed:#"Texture.png"];
self.view.backgroundColor = [UIColor colorWithPatternImage:bgimg];

UISegmentedControl does not respect divider images set for UIControlStateDisabled

I'm using the new UIAppearance API in iOS 5 to style a UISegmentedControl with custom graphics. I need to be able to set some segments to be disabled at times during execution, but the UIAppearance methods don't seem to allow me to set a divider image for the UIControlStateDisabled state.
I'm calling:
[[UISegmentedControl appearance] setDividerImage:disabledSelectedImage
forLeftSegmentState:UIControlStateDisabled
rightSegmentState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
where disabledSelectedImage is a resizable image from this resource:
Yet when I set the left segment to be disabled ([UISegmentedControl setEnabled:forSegmentAtIndex:]), the result is this:
You can clearly see that the UISegmentedControl has defaulted to use the UIControlStateNormal-UIControlStateNormal divider image.
It seems perfectly happy for me to set a background image using UIControlStateDisabled
[[UISegmentedControl appearance] setBackgroundImage:disabledImage
forState:UIControlStateDisabled
barMetrics:UIBarMetricsDefault];
(and respects the image I supply while in the disabled state) but not a divider image. Has anyone come across this or found a solution?
I've decided that this must be an iOS bug and have filed a radar with Apple. My solution to the problem for now is to remove segments, rather than disabling them.
A bit of an ugly workaround but i managed to fix it with the following until apple fixes it itself.
First you need to subclass UISegmentedControl and add the following:
#implementation MJSegmentedControl
- (void)layoutSubviews
{
[super layoutSubviews];
NSInteger cachedIndex = self.selectedSegmentIndex;
self.selectedSegmentIndex = 0;
self.selectedSegmentIndex = cachedIndex;
}
#end
I haven't had a need to use the appearance controls of iOS 5 yet but if all else fails you could add the resizable image as a child of the segmented control to cover up the ugliness. It's a hack, but it may work and will be relatively forwards-compatibile. Just be certain to set the autosizing masks appropriately.
I had the same issue and it really seems to be a bug. However I've found a solution (a workaround).
I've used XIB file with a controller. In XIB file segmented control was just placed and all of the customisations were done in -viewDidLoad method.
Then I've created a UIView subclass which represented entire view in the XIB. It made possible moving all view customisation code to the -awakeFromNib method of this UIView subclass. After moving this code the divider images were set properly.
As suggested by Fernando in this thread:
Customizing UISegmentedControl in iOS 5
You can try to dispatch your UISegmentedControl settings on the main queue via:
dispatch_async(dispatch_get_main_queue(),^{
// disable part of the segmented control
[self.eventScopeSegmentedControl setEnabled:NO forSegmentAtIndex:2];
});
I did this in viewDidLoad and it worked fine for a while but when my app is really busy at startup, this doesn't always work. I'm guessing there's a race condition that may still revert any settings you made when the appearance proxy goes to work.
I added another ugly hack to make this call in viewWillAppear (after the call to super:viewWillAppear) with a flag (set from viewWillLoad) to ensure this only runs once.
There is actually a pretty simple way to get this done. The current behavior is obviously a bug so this is not an ideal solution but simply a workaround that works beautifully. Namely, use an additional UIView as a "disabled visual cue".
The general steps:
Add a UIView as a sibling to the UISegmentedControl. Ensure the UIView is in front of the UISegmentedControl
Apply the desired color and a transparency to the UIView to match your app skin
Move the UIView to be exactly on top of the UISegmentedControl
Shape the UIView to have the exact size top of the UISegmentedControl
Apply a rounded corner to the UIView to mirror the exact shape of the UISegmentedControl
When the UISegmentedControl is supposed to be disabled, simply show the UIView and disable the user interaction on the UISegmentedControl.
When the UISegmentedControl is supposed to be enabled, simply hide the UIView and enable the user interaction on the UISegmentedControl.
In both cases do not change the UISegmentedControl.enabled property.
Note that it seems like a lot of steps but all of this can be coded in so to add support for disabling your custom UISegmentedControl becomes pretty much a 1 liner after you add this to a configure segmented control method.
Here is how my custom segmented control looks when applying this solution:
Enabled Segmented Control
"Disabled" Segmented Control
Here are some code snippets of interest:
Shape the UIView to match the UISegementedControl (load time configuration)
UISegmentedControl* segmentedControl = ...
//Segmented Control disabled visual cue view
UIView* view = ...
//Step #2
view.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6];
//Step #3 and #4
view.frame = segmentedControl.frame;
//Step #5
view.layer.cornerRadius = 5
view.clipsToBounds = YES;
//Ensure this is disabled by default
view.userInteractionEnabled = NO;
Enable/"Disable" UISegementedControl (runtime state change)
BOOL segmentedControlEnabled = ...
if(segmentedControlEnabled) {
segmentedControl.userInteractionEnabled = YES;
view.hidden = YES;
} else {
segmentedControl.userInteractionEnabled = NO;
view.hidden = NO;
}
That's it.
-

Resources