Make view transparent without seeing the text from bottom view - iphone

In the iPhone calendar app if you have 2 tiles overlaying ontop of each other the text from the bottom tile gets cut off and cannot be seen through the top transparent tile. How would I be able to keep the tiles transparent but not have the text from the bottom tiles show through to the top tiles?
Here is my code
APCalendarDayTile *tile = (APCalendarDayTile *)view;
CGFloat startPos = [APCalendarCurrentDayView yAxisForTime:[APCalendarCurrentDayView minutesToTime:tile.appointment.startDate]];
CGFloat endPos = [APCalendarCurrentDayView yAxisForTime:[APCalendarCurrentDayView minutesToTime:tile.appointment.endDate]];
tile.frame = CGRectMake(kLeftSideBuffer, startPos, (self.bounds.size.width - kLeftSideBuffer - kRightSideBuffer) , endPos - startPos);
tile.backgroundColor = [UIColor colorWithHexString:tile.appointment.appointmentColor]; <-- This also sets the alpha that makes it transparent.
tile.layer.borderColor = [UIColor colorWithHexString:tile.appointment.appointmentColor alpha:1.0].CGColor;
tile.layer.borderWidth = 1.0f;
Tile code
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
CALayer *layer = [self layer];
[layer setMasksToBounds:YES];
[layer setCornerRadius:kCornerRadius];
}
return self;
}
- (id)init
{
if (self = [super init]) {
self.clipsToBounds = YES;
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
tileTitle = [[UILabel alloc] init];
tileTitle.textColor = [UIColor blackColor];
tileTitle.backgroundColor = [UIColor clearColor];
tileTitle.font = [UIFont boldSystemFontOfSize:13.0f];
[tileTitle setAutoresizesSubviews:YES];
[tileTitle setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
tileDescription = [[UILabel alloc] init];
tileDescription.textColor = [UIColor blackColor];
tileDescription.backgroundColor = [UIColor clearColor];
tileDescription.font = [UIFont systemFontOfSize:11.0f];
tileDescription.lineBreakMode = UILineBreakModeTailTruncation;
[tileDescription setAutoresizesSubviews:YES];
[tileDescription setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self setAutoresizesSubviews:YES];
[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self addSubview:tileTitle];
[self addSubview:tileDescription];
}
return self;
}
- (void)layoutSubviews
{
CGRect myBounds = self.bounds;
CGSize stringSize = [tileTitle.text sizeWithFont:tileTitle.font];
if (myBounds.size.height <= 22.0) {
tileTitle.frame = CGRectMake(3, 0, myBounds.size.width, stringSize.height);
tileDescription.frame = CGRectMake(stringSize.width + 6, -1, myBounds.size.width, 14);
} else {
tileTitle.frame = CGRectMake(3, 0, myBounds.size.width, stringSize.height);
tileDescription.frame = CGRectMake(5, tileTitle.frame.size.height, myBounds.size.width, 14);
}
}
I would like the text in this photo to not show in the front tiles like the second photo.

#user1272965 is right insofar as each tile needs to know what tiles are above it, then you can get the text effect you're looking for by making the tile views a custom subclass and implementing their drawing manually:
Each tile will need access to tiles that are above it (or their frames at least):
NSInteger tileCount = [tilesAboveMine count];
CGRect framesAboveMine[tileCount];
for (int i=0; i<tileCount; i++) {
framesAboveMine[i] = [tilesAboveMine objectAtIndex:i].frame;
}
In drawRect of your tile view, draw the tile, and then draw the text clipped by the views above it:
// draw aspects of the tile not to be clipped, like the background image.
// the setup for clipping:
CGContextClipToRects(context, framesAboveMine, tileCount);
then draw the text, which will be clipped by the frames above
[myCalendarString drawAtPoint:CGPointMake(10,10)];

What about adding a UIView in between the two boxes? This middle UIView would cover up the letters (and have the same background color as the rear event), which solves your problem: no text coming through, but you'll see the color behind the other label!
You might be able to create the frame for this UIView using the frame attribute of the UILabel as well as the frame attribute of the frontmost calendar appointment.

I think the native calendar app squeezes tiles that occur at the very same start time as you do, but it also squeezes tiles whose times overlap, i.e. if the start time of the later tile is between the start and end time of the earlier tile.
But if you need your UI to draw these tiles overlapping, then there's no way around it: the obscured tile needs to know that it's underneath (starting after and overlapping), and it needs to either hide its labels, or reduce their alpha level.

Related

How to set the CAlayer bounds from my view controller for multiple subview

I am new to iOS programming. Please help me to sort out the following issue
I have one set of view controller(.h, .m and .XIB)
I have one set of view(.h&.m)
3.The view class is responsible for drawing gauge using
-->drawRect
-->CALayer and sublayers
In this view I have initialize method, and this method only i set the bounds for my layers and sublayers
here i am pasting some sort of code..
- (void)initialize {
CGFloat span = fmin(self.frame.size.width, self.frame.size.height);
frameLayerObject_ = [[OuterFrameLayer alloc] init];
frameLayer = [[CALayer layer] retain];
frameLayer.bounds = CGRectMake(250, 50, 710, 320);
frameLayer.position = self.frame.origin;
frameLayer.needsDisplayOnBoundsChange = YES;
frameLayer.delegate = self.frameLayerObject;
[self.layer addSublayer:frameLayer];
[frameLayer setNeedsDisplay];
self.minValue = 0.0;
self.maxValue = 100.0;
self.tickStartAngle = M_PI;
self.tickArcLength = M_PI
self.lineWidth = span / 200.0;
self.tickLength = span / 12.0;
self.minorTickLength = span / 16.0;
self.tickInset = self.lineWidth;
textLabel_ = [[UILabel alloc] initWithFrame:CGRectMake(0, 3.0 * self.frame.size.height / 10.0, self.frame.size.width, self.frame.size.height / 10.0)];
self.textLabel.textColor = [UIColor whiteColor];
self.textLabel.backgroundColor = [UIColor clearColor];
self.textLabel.font = [UIFont systemFontOfSize:span / 17.77];
[self addSubview:self.textLabel];
needleLayerObject_ = [[gaugeNeedle alloc] init];
self.needleLayerObject.tintColor = [UIColor whiteColor];
needleLayer = [[CALayer layer] retain];
needleLayer.bounds = self.bounds;
needleLayer.position =CGPointMake(((self.bounds.size.width / 2.0)), ((self.bounds.size.height / 2.0)));
needleLayer.needsDisplayOnBoundsChange = YES;
needleLayer.delegate = self.needleLayerObject;
[self.layer addSublayer:needleLayer];
[needleLayer setNeedsDisplay];
}
In my view controller, I have created 5 views in my xib and .h and i am assigning the same view class for all the 5 views.
Now the problem is, all 5 views differ in their position and bounds. But my drawRect view class has only one set of bounds for all views. So if I hardcode the bounds and position in initialize method with respect to one view, the remaining 4 views are getting affected. Please let me know if u know where i am going wrong.
Thanks in advance..
each of your five views should be executing the draw code relative to their own coordinate space, and there would be no problem. Use self.bounds as the rectangle in your drawing code and the five should all draw identically in their own space. Don't use self.frame, this is in the parent view's coordinate space, and introduces the problem you are experiencing.
You may also want to experiment with the clipsToBounds property, when this is set to YES a UIView can't draw beyond its own borders.

How to remove the saw tooth of UIView when using Quartz?

I want to add a head button to a view with some white space around it. My code is just like this:
// head button
UIButton * btnHead = [UIButton buttonWithType:UIButtonTypeCustom];
btnHead.frame = CGRectMake(0, 0, self.frame.size.width - property.userhead_cell_space / 2, self.frame.size.width - property.userhead_cell_space / 2);
btnHead.clipsToBounds = YES;
btnHead.layer.cornerRadius = btnHead.bounds.size.width / 2;
btnHead.layer.borderColor = [UIColor whiteColor].CGColor;
btnHead.layer.borderWidth = (isPad?4.0f:2.0f);
btnHead.layer.contentsScale = [[UIScreen mainScreen] scale];
btnHead.layer.backgroundColor = [UIColor whiteColor].CGColor;
[btnHead addTarget:self action:#selector(clickHead:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btnHead];
But alway it has some saw tooth around it . It may only one pixel. But it looks very terrible . Just like this:
Does anybody has some tip to remove the black saw tooth ?
Way late to the party but from past experience this is a bug with corner radius and borders. I've tried using it to create circular borders around views and have noticed similar bleeding.
One workaround is to create a view for the border and then a slightly inset view for the image.
UIView* border = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
border.backgroundColor = UIColor.whiteColor; // border color
border.layer.cornerRadius = border.bounds.size.width / 2;
border.layer.masksToBounds = YES;
// inset by the desired border width
UIImageView* image = [[UIImageView alloc] initWithFrame:CGRectInset(border.bounds, 2, 2)];
image.image = someImage;
image.layer.cornerRadius = image.bounds.size.width / 2;
image.layer.masksToBounds = YES;
[border addSubview:image];
[self addSubview:border];
If you want to avoid multiple views, you could add an additional background layer to the image view's layer for the border. That would need to have it's bounds negatively inset so that it drew around the image.

How to add View below other view in ViewController in iOS?

In my app i am creating view controller with mixed of UILabel and UITextview which want to be scrollable because the text are dynamic and also exceeds vertical screen size.
I am currently having Scrollview which has subviews as below. Views are created in Xcode 4.3 Storyboard.
UILabel1(Say Heading)
UITextView1(Dynamic text which can be any size)
UILabel2(Second Heading)
UITextView2(Dynamic text which can be any size)
and so on.
The problem is
When the UITextView1 has more content then it overlaps with UILabel2 which i don't want.
I would like to have UILabel1 on top of scrollView and UITextView1 below the UILabel1. UILabel2 below UITextView1 and so on.
What i have to do to achieve this?
EDIT
In Storyboard
![enter image description here][1]
In Simulator
![enter image description here][2]
Thanks for your help guys. Much appreciated.
Code
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[scrollView setScrollEnabled:YES];
[self.view addSubview:cockTailNameLabel];
[self.view insertSubview:txtIngredients belowSubview:cockTailNameLabel];
[self.view insertSubview:scrollView belowSubview:cockTailNameLabel];
//[scrollView]
[self.cockTailNameLabel setText:self.passcockTailName];
[_txtUse setText:self.passUse];
[_txtUse setEditable:NO];
[_txtUse setUserInteractionEnabled:NO];
CGRect useFrame = _txtUse.frame;
useFrame.size.height = _txtUse.contentSize.height;
_txtUse.frame = useFrame;
[txtIngredients setText:self.passIngredients];
[txtIngredients setEditable:NO];
[txtIngredients setUserInteractionEnabled:NO];
CGRect ingredientFrame = txtIngredients.frame;
ingredientFrame.size.height = txtIngredients.contentSize.height;
txtIngredients.frame = ingredientFrame;
[txtRecipe setText:self.passReceipe];
[txtRecipe setEditable:NO];
[txtRecipe setUserInteractionEnabled:NO];
CGRect recipeFrame = txtIngredients.frame;
recipeFrame.size.height = txtRecipe.contentSize.height;
txtRecipe.frame = recipeFrame;
[scrollView insertSubview:_txtUse belowSubview:cockTailNameLabel];
[scrollView insertSubview:titleIngredients belowSubview:_txtUse];
[scrollView insertSubview:txtIngredients belowSubview:titleIngredients];
[scrollView insertSubview:btnReceipe belowSubview:txtIngredients];
[scrollView insertSubview:btnHome belowSubview:txtIngredients];
[scrollView insertSubview:txtRecipe belowSubview:btnHome];
[scrollView insertSubview:btnfacebookShare belowSubview:txtRecipe];
[scrollView insertSubview:btnTwitterShare belowSubview:txtRecipe];
/*[scrollView addSubview:_txtUse];
[scrollView addSubview:titleIngredients];
[scrollView addSubview:txtIngredients];
[scrollView addSubview:btnReceipe];
[scrollView addSubview:btnHome];
[scrollView addSubview:txtRecipe];
[scrollView addSubview:btnfacebookShare];
[scrollView addSubview:btnTwitterShare];*/
[scrollView setContentSize:CGSizeMake(320, 1000)];
NSLog(#"RecipeName :%# ",passcockTailName);
}
In Storyboard or IB you can rearrange them freely.
In code you do - (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview.
In code (in viewDidLoad):
UIScrollView *scroll =[[UIScrollView alloc] initWithFrame:CGrectMake(0,0 320, 480)];
[self.view addSubview:scroll];
// code to init your UI object
UILabel *uilabel1 = [[UIlabel alloc] initWithFrame:CGrectMake(10,10, 100, 40)]; // example
uilabel1.font = [UIFont systemFontOfSize:10];
uilabel1.text = #"UILabel1";
uilabel1.backgroundColor = [UIColor clearColor];
//
//
UILabel *uilabel2 = [[UIlabel alloc] initWithFrame:CGrectMake(10, 10 + 10 + uilabel1.frame.origin.y, 100, 40)]; // example
uilabel2.font = [UIFont systemFontOfSize:10];
uilabel2.text = #"UILabel2";
uilabel2.backgroundColor = [UIColor clearColor];
//
//
[scroll addSubview:uilabel1];
[uilabel1 releale];
[scroll addSubview:uilabel2];
[uilabel2 releale];
//
//
// in end
float offset = 10.0 * 2; // offset between uiobjects, N - number of objects
scroll.contentSize = CGSizeMake (0, 0, uilabel1.frame.size.height + uilabel2.frame.size.height + offset, 320);
Note that you may set frame (and other properties) of yours uiobjects and adds it in order to descend.
You can tell next view to start from the end of the first view like so:
startYOfNextView = firstView.frame.origin.y position + firstView.frame.size.height;
Do the same for the rest of the other view. If your view has variable text length, you may need to precaculate the height of the string based on a specific font e.g.:
CGSize maxSize = CGSizeMake(widthOfView, 9999999);
CGSize expectedSize = CGSizeMake(stringVar sizeWithFont:[UIFont fontWithName:#"Arial"] withMaxSize:maxSize lineBreakMode:UILineBreakModeWordWrap);
then tell your dynamic view to use the height of expectedSize variable like so:
myDynamicView = [[UILabel alloc] initWithFrame:CGRectMake(..., expectedSize.height)];
Your issue is labels are coming on top of the textview (Overlapped), right?
In This case, you are modifying the height of the textview dynamically. If its just for display purpose you can set the text to a UILabel with numberOfLines = 0 instead of UITextview; As its added to scrollview adding a label will be fine.
Also you need to modify the origin.y of the remaining views so that it will be properly aligned. ie, secondView.origin.y = firstView.origin.y + firstView.size.height + offset. Likewise modify the origin of the remaining views wrt the just above view. So that its origin will lie outside the previous view frame.
In my case I had to project it over the parent view, so remember there is also something,
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview
I think adding a transparent UI button is the fastest solution. You could add it once and then show / hide whenever you need to disable all views below the current one (assuming the "current" view, say a popup, was added last to your superview).
Declare an instance variable UIButton* _parentDisablingOverlay; in your popup view, then add this in its init or layoutSubviews method:
_parentDisablingOverlay = [[UIButton alloc] initWithFrame:CGRectMake(0,0, self.superview.frame.size.width, self.superview.frame.size.height)];
[self.superview insertSubview:_parentDisablingOverlay belowSubview:self];
_parentDisablingOverlay.hidden = true;
Later on, when you want to show the popup and disable everything below:
- (void) open {
self.hidden = false;
_parentDisablingOverlay.hidden = false;
}
Similarly to close and re-enable everything:
- (void) close {
self.hidden = true;
_parentDisablingOverlay.hidden = true;
}

Tab Bar Controller Customization and "More" Problem

Here's the problem:
I have a tab bar controller customized so the tab item highlights are yellow instead of the default blue. What's wrong is that the "More" item comes up because I have too many tab items (removing them is not really an option), and this "more" is still blue.
I used classes I got from the internet to implement the customization. Here is the code:
// UITabBar+ColorExtensions.m
#implementation UITabBar (ColorExtensions)
- (void)recolorItemsWithColor:(UIColor *)color shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset shadowBlur:(CGFloat)shadowBlur
{
CGColorRef cgColor = [color CGColor];
CGColorRef cgShadowColor = [shadowColor CGColor];
for (UITabBarItem *item in [self items])
if ([item respondsToSelector:#selector(selectedImage)] &&
[item respondsToSelector:#selector(setSelectedImage:)] &&
[item respondsToSelector:#selector(_updateView)])
{
CGRect contextRect;
contextRect.origin.x = 0.0f;
contextRect.origin.y = 0.0f;
contextRect.size = [[item selectedImage] size];
// Retrieve source image and begin image context
UIImage *itemImage = [item image];
CGSize itemImageSize = [itemImage size];
CGPoint itemImagePosition;
itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) / 2);
UIGraphicsBeginImageContext(contextRect.size);
CGContextRef c = UIGraphicsGetCurrentContext();
// Setup shadow
CGContextSetShadowWithColor(c, shadowOffset, shadowBlur, cgShadowColor);
// Setup transparency layer and clip to mask
CGContextBeginTransparencyLayer(c, NULL);
CGContextScaleCTM(c, 1.0, -1.0);
CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [itemImage CGImage]);
// Fill and end the transparency layer
CGContextSetFillColorWithColor(c, cgColor);
contextRect.size.height = -contextRect.size.height;
CGContextFillRect(c, contextRect);
CGContextEndTransparencyLayer(c);
// Set selected image and end context
[item setSelectedImage:UIGraphicsGetImageFromCurrentImageContext()];
UIGraphicsEndImageContext();
// Update the view
[item _updateView];
}
}
#end
and the controller:
#implementation AATabBarController
- (void)viewDidLoad {
[super viewDidLoad];
// Put in a background
CGRect frame = CGRectMake(0.0, 0, self.view.bounds.size.width, 48);
backgroundView = [[UIView alloc] initWithFrame:frame];
[backgroundView setBackgroundColor:[UIColor colorWithRed:0.0
green:0.0
blue:0.0
alpha:0.1]];
[self.tabBar insertSubview:backgroundView atIndex:0];
}
-(void)dealloc {
[backgroundView release];
[super dealloc];
}
-(void)updateTabColor:(UIColor *)color {
// Recolor the tab bar
[self.tabBar recolorItemsWithColor:color shadowColor:[UIColor blackColor] shadowOffset:CGSizeMake(0.0f, -1.0f) shadowBlur:3.0f];
}
-(void)updateBackgroundColor:(UIColor *)color {
// Update the background color
[backgroundView setBackgroundColor:color];
}
#end
Does anyone know what I should do so the "more" tab item is the customized color?
That code looks very similar to the code in this question. I know you're asking a different question here (the linked Q asks "will my app be rejected" to which the answer is "yes"). Nevertheless, you're using the same private UITabBarItem methods, so it's unlikely that anyone can give you a reliable answer.

iPhone UIScrollView - how to get an image title

I'm ATTEMPTING to learn UIScrollview using Apple's Docs and their sample code http://developer.apple.com/iphone/library/samplecode/Scrolling/index.html but something SO simple is escaping me.
How do you tell what image is currently on the screen, so that if I selected one of the images in the horizontal scrolling view, how would I get the filename of the image, or even a pointer in the array, to then do something further with the image?
I thought with Page Control enable I might be able to find a page # and map it to the image. I thought about counting deceleration to count pages, but a flick no full enough will increment it and give a false number.
The last thing I could think of is to get contentOffSet and divide by image size which will give a 1, 2, 3 and I could point to the array (too tired to try tonight... thought I might ask before I waste a lot more time ;-) ).
Any other ideas? I thought there would be a method somewhere that they use in the photo album app.
PS: Here's the code:
- (void)layoutScrollImages
{
UIImageView *view = nil;
NSArray *subviews = [scrollView1 subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UIImageView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
// set the content size so it can be scrollable
[scrollView1 setContentSize:CGSizeMake((kNumImages * kScrollObjWidth), [scrollView1 bounds].size.height)];
}
- (void)viewDidLoad
{
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
// 1. setup the scrollview for multiple images and add it to the view controller
//
// note: the following can be done in Interface Builder, but we show this in code for clarity
[scrollView1 setBackgroundColor:[UIColor blackColor]];
[scrollView1 setCanCancelContentTouches:NO];
scrollView1.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scrollView1.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView1.scrollEnabled = YES;
// pagingEnabled property default is NO, if set the scroller will stop or snap at each photo
// if you want free-flowing scroll, don't set this property.
scrollView1.pagingEnabled = YES;
// load all the images from our bundle and add them to the scroll view
//NSUInteger i;
for (i = 1; i <= kNumImages; i++)
{
NSString *imageName = [NSString stringWithFormat:#"Card %d.png", i];
UIImage *image = [UIImage imageNamed:imageName];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// setup each frame to a default height and width, it will be properly placed when we call "updateScrollList"
CGRect rect = imageView.frame;
rect.size.height = kScrollObjHeight;
rect.size.width = kScrollObjWidth;
imageView.frame = rect;
imageView.tag = i; // tag our images for later use when we place them in serial fashion
[scrollView1 addSubview:imageView];
[imageView release];
}
[self layoutScrollImages]; // now place the photos in serial layout within the scrollview
This was easy after a good sleep!
CGPoint p = scrollView1.contentOffset;
NSLog(#"x = %f, y = %f", p.x, p.y);
Now just divide by 320 (if horizontal and full screen image) and add 1 (because it starts at 0).
Hope this helps someone else!
Paul