UIBezierPath on UiView doesn't update until scroll the tableview - objective-c

I have added UIView to cell of Tableview and given the beizer path to bottom and right by following:
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(0,
0,
cell.bgView.frame.size.width -2+ shadowSize,
cell.bgView.frame.size.height+1 + shadowSize)];
cell.bgView.layer.masksToBounds = NO;
cell.bgView.layer.shadowColor = [[UIColor colorWithRed:186.0/255.0 green:0.0/255.0 blue:231.0/255.0 alpha:1.0f]CGColor];
cell.bgView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
cell.bgView.layer.shadowOpacity = 0.7f;
cell.bgView.layer.shadowPath = shadowPath.CGPath;
First time, when tableview reload, bezier path doesn't load properly
but when I scroll up/down then it looks perfectly
.
I have tried with every possible solution i.e.
dispatch_async(dispatch_get_main_queue(), ^{
[self.topRatedTable reloadData];
});
or
[cell layoutIfNeeded];
[cell updateConstraintsIfNeeded];
But nothing works.

Your cell and its subviews (like, I assume, bgView) don't have their final frames until layout has run. If your path-creating code is in your tableView:cellForRowAtIndexPath: method, then you are accessing cell.bgView.frame before layout has run.
In your custom UITableViewCell subclass, override layoutSubviews and set the shadowPath after calling super:
// In your custom `UITableViewCell` subclass:
- (void)layoutSubviews {
[super layoutSubviews];
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(0,
0,
cell.bgView.frame.size.width -2+ shadowSize,
cell.bgView.frame.size.height+1 + shadowSize)];
self.bgView.layer.shadowPath = shadowPath.CGPath;
}

Use this:
override func draw(_ rect: CGRect) {
super.draw(rect)
// UIBezierPath job
}

Related

TableViewCell borders created disappear when highlighted

I found on this forum a method that can create a border from ether side of a view, with I applying on my labels in my cell when it created in cellForRowAtIndexPath
the problem is when i select the cell the lines I created are gone, when I deselect the cell I can see the line again.
I had an issue when the cell is selected the labels text color in the cell get change to white but u fixed this with the method setSelected , with help me save cell labels colors before it selected. the problem is it dosent work the same way
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
[self saveTextColors];
[self saveLeftBoarders];
}
- (void)saveLeftBoarders {
[self addLeftBorderWithColor:[UIColor grayColor] Width:1.0 andView:amountV];
[self addLeftBorderWithColor:[UIColor grayColor] Width:1.0 andView:amountPay];
[self addLeftBorderWithColor:[UIColor grayColor] Width:1.0 andView:date];
[self addLeftBorderWithColor:[UIColor grayColor] Width:1.0 andView:month];
}
- (void)saveTextColors {
UIColor *leftPayColor = self.leftPay.textColor;
UIColor *amountVColor = self.amountV.textColor;
UIColor *monthColor = self.month.textColor;
UIColor *amountPayColor = self.amountPay.textColor;
UIColor *dateColor = self.date.textColor;
self.leftPay.highlightedTextColor = leftPayColor;
self.amountV.highlightedTextColor = amountVColor;
self.month.highlightedTextColor = monthColor;
self.amountPay.highlightedTextColor = amountPayColor;
self.date.highlightedTextColor = dateColor;
}
- (void)addLeftBorderWithColor:(UIColor *)color Width:(CGFloat) borderWidth andView:(UIView *)view {
UIView *border = [UIView new];
border.backgroundColor = color;
border.frame = CGRectMake(0, 0, borderWidth, view.frame.size.height);
[border setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleRightMargin];
[view addSubview:border];
}
this code is from the cell.m
in the method cellForRowAtIndexPath I run the same method to create the border line.
as I type this question I saw in the debugView that this creates a problem of creating that border over and over again but is still not visible,
after deleting the border creation in the cell.m I saw in the debug view hierarchy that the border is there ( from it creation in cellForRowAtIndexPath) but it not visible.
what is the problem ?

UIButton displaying a triangle

I have a UIButton and i want it to display a triangle. Is there a function to make it a triangle? Since im not using a UIView class im not sure how to make my frame a triangle.
ViewController(m):
- (IBAction)makeTriangle:(id)sender {
UIView *triangle=[[UIView alloc] init];
triangle.frame= CGRectMake(100, 100, 100, 100);
triangle.backgroundColor = [UIColor yellowColor];
[self.view addSubview: triangle];
Do i have to change my layer or add points and connect them to make a triangle with CGRect?
If im being unclear or not specific add a comment. Thank you!
A button is a subclass of UIView, so you can make it any shape you want using a CAShape layer. For the code below, I added a 100 x 100 point button in the storyboard, and changed its class to RDButton.
#interface RDButton ()
#property (strong,nonatomic) UIBezierPath *shape;
#end
#implementation RDButton
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.titleEdgeInsets = UIEdgeInsetsMake(30, 0, 0, 0); // move the title down to make it look more centered
self.shape = [UIBezierPath new];
[self.shape moveToPoint:CGPointMake(0,100)];
[self.shape addLineToPoint:CGPointMake(100,100)];
[self.shape addLineToPoint:CGPointMake(50,0)];
[self.shape closePath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = self.shape.CGPath;
shapeLayer.fillColor = [UIColor yellowColor].CGColor;
shapeLayer.strokeColor = [UIColor blueColor].CGColor;
shapeLayer.lineWidth = 2;
[self.layer addSublayer:shapeLayer];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self.shape containsPoint:[touches.anyObject locationInView:self]])
[super touchesBegan:touches withEvent:event];
}
The touchesBegan:withEvent: override restricts the action of the button to touches within the triangle.
A view's frame is always a rect, which is a rectangle. Even if you apply a transform to it so it no longer looks like a rectangle, the view.frame property will still be a rectangle -- just the smallest possible rectangle that contains the new shape you have produced.
So if you want your UIButton to look like a triangle, the simplest solution is probably to set its type to UIButtonTypeCustom and then to set its image to be a png which shows a triangle and is transparent outside of the triangle.
Then the UIButton itself will actually be rectangle, but will look like a triangle.
If you want to get fancy, you can also customize touch delivery so that touches on the transparent part of the PNG are not recognized (as I believe they would be by default), but that might be a bit trickier.

UIScrollView return to its original place

I have the following code:
float yOffset = activeTextView.frame.origin.y - keyboardSize.height + 55;
CGPoint scrollPoint = CGPointMake(0.0, yOffset);
[scrollView setContentOffset:scrollPoint animated:YES];
This animates the scrollView in - (void)keyboardWasShown:(NSNotification *)notification
I am trying to return the scrollView to it's original location after the hiding the keyboard like this:
- (void) keyboardWillHide:(NSNotification *)notification {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
But it doesn't work!
How can I return the UIScrollView and actually the whole screen to its original location so the user will see what he saw before animation of the scrollview?
In your keyboardWasShown: method, you're setting the contentOffset property ([scrollView setContentOffset:] is equivalent to scrollView.contentOffset). However, in keyboardWillHide:, you're setting contentInset, which is something completely different (essentially, it's the amount of internal padding of the scroll view's content). Try
scrollView.contentOffset = CGPointZero; // non-animated by default
or
[scrollView setContentOffset:CGPointZero animated:YES]; // animated
Also, as NSResponder mentioned, make sure your keyboardWillHide: method is being called.

Initial frame to use for UITableViewCell's backgroundView

I'm currently initializing my UITableViewCell's backgroundView's frame with self.frame. This seems to work fine for device orientation changes (cell background fills entire cell, looks fine etc). What's a better frame to use (if any)?
Edit #1: I've also initialized the backgroundView with CGRectZero as the frame. This seems to make no difference (UITableViewCells and backgroundViews function just fine in all interface orientations).
I've also tested setting the autoresizingMask property of the backgroundView. This made no difference either. I'd just like to understand what (if anything) is affected by the backgroundViews initial frame.
Assuming that you are trying to add a UIImageView as backgroundView and that you are trying to resize that imageView, here's my experience:
It seems to be impossible to change the frame of UITableViewCell.backgroundView (or at least not a thing Apple recommends, hence it's not mentioned in the documentation). To use a custom sized UIImageView, eg with a resizable UIImage, as background in a UITableViewCell, I do the following:
1) Create a UIImageView and set its image property to an image of your wish.
2) Add the UIImageView as a subview of the UITableViewCell using the addSubview: message.
3) Send the UIImageView to the back using sendSubviewToBack: message.
This puts your UIImageView behind any other added subviews and you are now able to manipulate the frame of your 'backgroundView' (aka the imageview).
To make sure the imageview will fit the tableViewCell's frame, use cell.frame's height and width properties when calculating the height of your imageview.
If you develop custom table view cell the solution is to adjust the frame in layoutSubviews method. Here is custom UITableViewCell from one of my projects, I needed 10 points margin from left:
#import "TETopicCell.h"
#import "UIColor+Utils.h"
#implementation TETopicCell
#synthesize topicTitleLabel = _topicTitleLabel;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"theme_btn_yellow"]];
bgImageView.contentMode = UIViewContentModeTopLeft;
bgImageView.frame = CGRectMake(0.0f, 0.0f, 239.0f, 42.0f);
self.backgroundView = bgImageView;
_topicTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(46.0f, 0.0f, 206.0f, 42.0f)];
_topicTitleLabel.backgroundColor = [UIColor clearColor];
_topicTitleLabel.textColor = [UIColor colorWithR:116 G:74 B:1];
[self.contentView addSubview:_topicTitleLabel];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// update frame here
self.backgroundView.frame = CGRectOffset(self.backgroundView.frame, 10.0f, 0.0f);
// and here
if (self.selectedBackgroundView){
self.selectedBackgroundView.frame = CGRectOffset(self.selectedBackgroundView.frame, 10.0f, 0.0f);
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (selected) {
UIImageView *selectedImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"theme_btn_red"]];
selectedImageView.contentMode = UIViewContentModeTopLeft;
selectedImageView.frame = CGRectMake(0.0f, 0.0f, 239.0f, 42.0f);
self.selectedBackgroundView = selectedImageView;
[_topicTitleLabel setTextColor:[UIColor colorWithR:255 G:211 B:211]];
}
else {
self.selectedBackgroundView = nil;
[_topicTitleLabel setTextColor:[UIColor colorWithR:116 G:74 B:1]];
}
}
#end

Why do all backgrounds disappear on UITableViewCell select?

My current project's UITableViewCell behavior is baffling me. I have a fairly straightforward subclass of UITableViewCell. It adds a few extra elements to the base view (via [self.contentView addSubview:...] and sets background colors on the elements to have them look like black and grey rectangular boxes.
Because the background of the entire table has this concrete-like texture image, each cell's background needs to be transparent, even when selected, but in that case it should darken a bit. I've set a custom semi-transparent selected background to achieve this effect:
UIView *background = [[[UIView alloc] initWithFrame:self.bounds] autorelease];
background.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
background.opaque = NO;
[self setSelectedBackgroundView:background];
And although that yields the right look for the background, a weird side effect happens when I select the cell; all other backgrounds are somehow turnt off. Here's a screenshot. The bottom cell looks like it should and is not selected. The top cell is selected, but it should display the black and grey rectangular areas, yet they are gone!
Who knows what's going on here and even more important: how can I correct this?
What is happening is that each subview inside the TableViewCell will receive the setSelected and setHighlighted methods. The setSelected method will remove background colors but if you set it for the selected state it will be corrected.
For example if those are UILabels added as subviews in your customized cell, then you can add this to the setSelected method of your TableViewCell implementation code:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
self.textLabel.backgroundColor = [UIColor blackColor];
}
where self.textLabel would be whatever those labels are that are shown in the picture above
I'm not sure where your adding your selected view, I usually add it in the setSelected method.
Alternatively, you can subclass the UILabel and override the setHighlighted method like so:
-(void)setHighlighted:(BOOL)highlighted
{
[self setBackgroundColor:[UIColor blackColor]];
}
The cell highlighting process can seem complex and confusing if you don't know whats going on. I was thoroughly confused and did some extensive experimentation. Here's the notes on my findings that may help somebody (if anyone has anything to add to this or refute then please comment and I will endeavour to confirm and update)
In the normal “not selected” state
The contentView (whats in your XIB unless you coded it otherwise) is drawn normally
The selectedBackgroundView is HIDDEN
The backgroundView is visible (so provided your contentView is transparent you see the backgroundView or (if you have not defined a backgroundView you'll see the background colour of the UITableView itself)
A cell is selected, the following occurs immediately with-OUT any animation:
All views/subviews within the contentView have their backgroundColor cleared (or set to transparent), label etc text color's change to their selected colour
The selectedBackgroundView becomes visible (this view is always the full size of the cell (a custom frame is ignored, use a subview if you need to). Also note the backgroundColor of subViews are not displayed for some reason, perhaps they're set transparent like the contentView). If you didn't define a selectedBackgroundView then Cocoa will create/insert the blue (or gray) gradient background and display this for you)
The backgroundView is unchanged
When the cell is deselected, an animation to remove the highlighting starts:
The selectedBackgroundView alpha property is animated from 1.0 (fully opaque) to 0.0 (fully transparent).
The backgroundView is again unchanged (so the animation looks like a crossfade between selectedBackgroundView and backgroundView)
ONLY ONCE the animation has finished does the contentView get redrawn in the "not-selected" state and its subview backgroundColor's become visible again (this can cause your animation to look horrible so it is advisable that you don't use UIView.backgroundColor in your contentView)
CONCLUSIONS:
If you need a backgroundColor to persist through out the highlight animation, don't use the backgroundColor property of UIView instead you can try (probably with-in tableview:cellForRowAtIndexPath:):
A CALayer with a background color:
UIColor *bgColor = [UIColor greenColor];
CALayer* layer = [CALayer layer];
layer.frame = viewThatRequiresBGColor.bounds;
layer.backgroundColor = bgColor.CGColor;
[cell.viewThatRequiresBGColor.layer addSublayer:layer];
or a CAGradientLayer:
UIColor *startColor = [UIColor redColor];
UIColor *endColor = [UIColor purpleColor];
CAGradientLayer* gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = viewThatRequiresBGColor.bounds;
gradientLayer.colors = #[(id)startColor.CGColor, (id)endColor.CGColor];
gradientLayer.locations = #[[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:1]];
[cell.viewThatRequiresBGColor.layer addSublayer:gradientLayer];
I've also used a CALayer.border technique to provide a custom UITableView seperator:
// We have to use the borderColor/Width as opposed to just setting the
// backgroundColor else the view becomes transparent and disappears during
// the cell's selected/highlighted animation
UIView *separatorView = [[UIView alloc] initWithFrame:CGRectMake(0, 43, 1024, 1)];
separatorView.layer.borderColor = [UIColor redColor].CGColor;
separatorView.layer.borderWidth = 1.0;
[cell.contentView addSubview:separatorView];
When you start dragging a UITableViewCell, it calls setBackgroundColor: on its subviews with a 0-alpha color. I worked around this by subclassing UIView and overriding setBackgroundColor: to ignore requests with 0-alpha colors. It feels hacky, but it's cleaner than any of the other solutions I've come across.
#implementation NonDisappearingView
-(void)setBackgroundColor:(UIColor *)backgroundColor {
CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
if (alpha != 0) {
[super setBackgroundColor:backgroundColor];
}
}
#end
Then, I add a NonDisappearingView to my cell and add other subviews to it:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UIView *background = [cell viewWithTag:backgroundTag];
if (background == nil) {
background = [[NonDisappearingView alloc] initWithFrame:backgroundFrame];
background.tag = backgroundTag;
background.backgroundColor = backgroundColor;
[cell addSubview:background];
}
// add other views as subviews of background
...
}
return cell;
}
Alternatively, you could make cell.contentView an instance of NonDisappearingView.
My solution is saving the backgroundColor and restoring it after the super call.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
UIColor *bgColor = self.textLabel.backgroundColor;
[super setSelected:selected animated:animated];
self.textLabel.backgroundColor = bgColor;
}
You also need to do the same thing with -setHighlighted:animated:.
Found a pretty elegant solution instead of messing with the tableView methods. You can create a subclass of UIView that ignores setting its background color to clear color. Code:
class NeverClearView: UIView {
override var backgroundColor: UIColor? {
didSet {
if UIColor.clearColor().isEqual(backgroundColor) {
backgroundColor = oldValue
}
}
}
}
Obj-C version would be similar, the main thing here is the idea
I created a UITableViewCell category/extension that allows you to turn on and off this transparency "feature".
You can find KeepBackgroundCell on GitHub
Install it via CocoaPods by adding the following line to your Podfile:
pod 'KeepBackgroundCell'
Usage:
Swift
let cell = <Initialize Cell>
cell.keepSubviewBackground = true // Turn transparency "feature" off
cell.keepSubviewBackground = false // Leave transparency "feature" on
Objective-C
UITableViewCell* cell = <Initialize Cell>
cell.keepSubviewBackground = YES; // Turn transparency "feature" off
cell.keepSubviewBackground = NO; // Leave transparency "feature" on
Having read through all the existing answers, came up with an elegant solution using Swift by only subclassing UITableViewCell.
extension UIView {
func iterateSubViews(block: ((view: UIView) -> Void)) {
for subview in self.subviews {
block(view: subview)
subview.iterateSubViews(block)
}
}
}
class CustomTableViewCell: UITableViewCell {
var keepSubViewsBackgroundColorOnSelection = false
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
// MARK: Overrides
override func setSelected(selected: Bool, animated: Bool) {
if self.keepSubViewsBackgroundColorOnSelection {
var bgColors = [UIView: UIColor]()
self.contentView.iterateSubViews() { (view) in
guard let bgColor = view.backgroundColor else {
return
}
bgColors[view] = bgColor
}
super.setSelected(selected, animated: animated)
for (view, backgroundColor) in bgColors {
view.backgroundColor = backgroundColor
}
} else {
super.setSelected(selected, animated: animated)
}
}
override func setHighlighted(highlighted: Bool, animated: Bool) {
if self.keepSubViewsBackgroundColorOnSelection {
var bgColors = [UIView: UIColor]()
self.contentView.iterateSubViews() { (view) in
guard let bgColor = view.backgroundColor else {
return
}
bgColors[view] = bgColor
}
super.setHighlighted(highlighted, animated: animated)
for (view, backgroundColor) in bgColors {
view.backgroundColor = backgroundColor
}
} else {
super.setHighlighted(highlighted, animated: animated)
}
}
}
All we need is to override the setSelected method and change the selectedBackgroundView for the tableViewCell in the custom tableViewCell class.
We need to add the backgroundview for the tableViewCell in cellForRowAtIndexPath method.
lCell.selectedBackgroundView = [[UIView alloc] init];
Next I have overridden the setSelected method as mentioned below.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
UIImageView *lBalloonView = [self viewWithTag:102];
[lBalloonView setBackgroundColor:[[UIColor hs_globalTint] colorWithAlphaComponent:0.2]];
UITextView *lMessageTextView = [self viewWithTag:103];
lMessageTextView.backgroundColor = [UIColor clearColor];
UILabel *lTimeLabel = [self viewWithTag:104];
lTimeLabel.backgroundColor = [UIColor clearColor];
}
Also one of the most important point to be noted is to change the tableViewCell selection style. It should not be UITableViewCellSelectionStyleNone.
lTableViewCell.selectionStyle = UITableViewCellSelectionStyleGray;