How to make UICollectionheader transparent while scrolling up? - objective-c

Please help me to make the UICollectionview header transparent while we scroll the collection view up. I have used UICollectionReusableView and set it background as clear color, but no use. WHILE MOVING UP THE HEADER ( SHOWN IN BLACK COLOR) SHOULD BECOME TRANSPARENT.... THAT IS THE ORANGE BACKGROUND SHOULD BE SLIGHTLY VISIBLE....

Not enough info, but I'll try to answer
First, you should keep pointer to your header view constantly
#property UICollectionReusableView *collectionHeaderView;
Create it once in viewDidLoad and than use it as a header view (whatever that means) in your collection view.
Next, to change collor on dragging up you'll need delegate method.
UICollectionViewDelegate conforms to UIScrollViewDelegate, so you can use its method
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
CGPoint translation = [scrollView.panGestureRecognizer translationInView:scrollView.superview];
if(translation.y > 0)
{
//dragging down
collectionHeaderView.backgroundColor = <#Whatever you need#>;
} else
{
// dragging up
collectionHeaderView.backgroundColor = [UIColor clearColor];
}
}
Special thanks to #mvielbut for answer
Update
According to comments to mvielbut's answer, this method is not always reliable, so you can use another approach (you'll need to check offset.y instead).

Related

Header views height for grouped UITableView on iOS7

I'm building my settings screen and using a grouped table view.
When trying to set the headers I see spacing above my header view.
I double checked and I do pass the correct view height in -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section.
Here is a screenshot of this behavior:
You can see my view with the title (VIBRATE, SILENT MODE) in it and it's darker bg color and the brighter space above it.
After much searching, I have finally found a fix for this. The tableview's delegate needs to implement heightForFooterInSection and return a very small number. Returning 0 defaults to the same spacing that was causing the extra spaces.
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return CGFLOAT_MIN;
}
Try this:
- (void)viewWillAppear:(BOOL)animated{
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 1;
UIView *headerView = [[UIView alloc] initWithFrame:frame];
[self.tableView setTableHeaderView:headerView];
}
This is pretty much the same as Casey's response, however, it is a bit cleaner as it doesn't require implementing a delegate method. When you are setting up your table view, simply set the property sectionFooterHeight to 0. It accomplishes the same thing with less code (and no DBL_MIN oddness).
tableView.sectionFooterHeight = 0.0;
Pretty sure it is just a simple hack. But an easy way to do it is to write this function:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 48.0f; // header height
}
to customize its height.
Pretty sure there are other ways to do it, that I don't know of.
It seems that Apple made a conscious design decision to make grouped table views have extra space on top. Try adjusting the contentInset of the UITableView. See my answer here
Swift 2.2 version:
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.min
}

Resize UICollectionView cells after their data has been set

My UICollectionView cells contain UILabels with multiline text. I don't know the height of the cells until the text has been set on the label.
-(CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
This was the initial method I looked at to size the cells. However, this is called BEFORE the cells are created out of the storyboard.
Is there a way to layout the collection and size the cells AFTER they have been rendered, and I know the actual size of the cell?
I think your are looking for the invalidateLayout method you can call on the .collectionViewLayout property of your UICollectionView. This method regenerates your layout, which in your case means also calling -collectionView: layout: sizeForItemAtIndexPath:, which is the right place to reflect your desired item size. Jirune points the right direction on how to calculate them.
An example for the usage of invalidateLayout can be found here. Also consult the UICollectionViewLayout documentation on that method:
Invalidates the current layout and triggers a layout update.
Discussion:
You can call this method at any time to update the layout information. This method invalidates the layout of the collection view itself and returns right away. Thus, you can call this method multiple times from the same block of code without triggering multiple layout updates. The actual layout update occurs during the next view layout update cycle.
Edit:
For storyboard collection view which contains auto layout constraints, you need to override viewDidLayoutSubviews method of UIViewController and call invalidateLayout collection view layout in this method.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[yourCollectionView.collectionViewLayout invalidateLayout];
}
subclass UICollectionViewCell and override layoutSubviews like this
hereby you will anchor cell leading and trailing edge to collectionView
override func layoutSubviews() {
super.layoutSubviews()
self.frame = CGRectMake(0, self.frame.origin.y, self.superview!.frame.size.width, self.frame.size.height)
}
Hey in the above delegate method itself, you can calculate the UILabel size using the below tricky way and return the UICollectionViewCell size based on that calculation.
// Calculate the expected size based on the font and
// linebreak mode of your label
CGSize maximumLabelSize = CGSizeMake(9999,9999);
CGSize expectedLabelSize =
[[self.dataSource objectAtIndex:indexPath.item]
sizeWithFont:[UIFont fontWithName:#"Arial" size:18.0f]
constrainedToSize:maximumLabelSize
lineBreakMode:NSLineBreakByWordWrapping];
- (void)viewDidLoad {
self.collectionView.prefetchingEnabled = NO;
}
In iOS 10, prefetchingEnabled is YES by default. When YES, the collection view requests cells in advance of when they will be displayed. It leads to crash in iOS 10

How to change the background color of an NSPopupButton?

I am trying to tackle a problem which sounds pretty simple: changing the background color of an NSPopupButton.
Interface Builder only allows changing the style to a pre-defined one and doesn't allow changing the background color. Also, setting up an IBOutlet didn't help since NSPopupButton doesn't have a setBackgroundColor method.
I also tried subclassing NSPopupButton to override the drawRect method. Here's what I have tried:
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] setFill];
NSRectFill(dirtyRect);
}
This draws a red rectangle over the NSPopupButton rather than setting it as a background color.
Any ideas on how to go about solving this?
You should create a subclass of NSPopUpButtonCell, then override
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView
NSPopupButtonCell is a subclass of NSButtonCell which defines several methods for drawing individual cell components, eg bezel, title, image.
You can then expand the NSPopupButton and change its cell subclass to your new subclass and it should use your drawing methods.
Cocoa primarily uses NSCell to handle drawing, unlike iOS
Swift version of #DanBrooker's answer. The example shows setting a background color
class PopUpButtonCell: NSPopUpButtonCell {
override func drawBezel(withFrame frame: NSRect, in controlView: NSView) {
guard let context = NSGraphicsContext.current?.cgContext else { return }
NSColor.red.setFill() // NSColor.white.setFill()
context.fill(frame)
}
}
The button is draw by the system, so there isn't a real way to set the background color in a way that the system draws it like you want.The only thing that you can do is to draw it in the drawRect method, also drawing the title, and drawing a portion of the rectangle.

Setting background color of UIView subclass doesn't work

I'm trying to change background color of one of my UIView subclasses. For some reason self.backgroundColor = [UIColor whiteColor];doesn't do anything when I put it in my - (id)initWithFrame:(CGRect)framemethod inside the view. The view is always black. I have also tried self.myView.backgroundColor ... from my view's controller, but that didn't work either. Any ideas on what I'm doing wrong?
The relevant code looks like this:
[...]
#interface PaperView : UIView
[....]
[...]
#implementation PaperView
[...]
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[...]
// Initialization code
self.backgroundColor = [UIColor whiteColor]; // This doesn't do anything, the view is always black.
}
return self;
}
If this view is being unarchived from a xib, you need to override -initWithCoder:. -initWithFrame: is only invoked if you are creating your view programmatically.
I had this same problem. I hooked into layoutSubviews() and it worked ok:
override func layoutSubviews() {
super.layoutSubviews()
self.backgroundColor = UIColor.clearColor()
}
This is indicative of the view not having a frame set to it. I recommend setting a breakpoint in your initWithFrame: to verify that its being called. If you were to call, say, ... = [UIView alloc] init], then that could be the source of your problem.
EDIT
If initWithFrame: is in fact being called, it's possible that the view is being covered by another view giving the appearance that it's not working (since you don't see it) or that the view itself is hidden.
Another way to troubleshoot is to override the backgroundColor property and set a breakpoint. Find out what else, in the callstack, is changing the color.
You may use self.layer.backgroundColor instead:
mySubclassedView.layer.backgroundColor = UIColor.green.cgColor
In what method do you call self.myView.backgroundColor?
Are you sure, that it's after viewDidLoad: ?
But, have no idea what is wrong with your first method.
Could you show more code?
I just had this exact same problem. The background color did not show even though I set the correct frame and set the background color to white in my custom init method, as well as in my viewWillAppear method. I also verified that nothing was covering it.
Then I found the solution: I set the background color in viewDidAppear instead, and all was fine:
- (void)viewDidAppear:(BOOL)animated
{
[self.view setBackgroundColor:[UIColor whiteColor]];
self.view.frame = _viewFrame;
}
(The _viewFrame CGRect was passed in to my init method.)
Another option is to set it in - (void)viewDidLayoutSubviews, depending on when and how exactly you want to set your background color.
To be entirely honest, I don't understand (yet) why setting the background color in viewDidAppear worked while it didn't work in the init method, and the code for setting it was identical in both places.
Hope this helps,
Erik
UPDATE:
It does have something to do with view's frame. When I set my view's frame in my init method, then setting the background color in the viewDidAppear no longer has the desired effect. This is even the case if I set my view's frame after my view build method that creates the sub views. So the real mystery is: between the point where I am done creating my view and the point where it is displayed, what in the view's life cycle is causing the view's frame to be reset to something that is incorrect?
So, the answer really is: it will work as long as your frame is set correctly and your view is visible. Just check your view's frame throughout the view's lifecycle to make sure it's correct.
Tricky...
Why can't you implement self.backgroundColor = [UIColor whiteColor] in -viewDidLoad method instead of -initWithFrame? Then try self.backgroundColor = [UIColor whiteColor]; as well as self.myView.backgroundColor to see which works.

UIView subclass draws background despite completely empty drawRect: - why?

So, I have a custom UIView subclass which enables drawing of rounded edges. The thing draws perfectly, however the background always fills the whole bounds, despite clipping to a path first. The border also draws above the rectangular background, despite the fact that I draw the border in drawRect: before the background. So I removed the whole content of drawRect:, which is now virtually empty - nevertheless the background gets drawn!
Anybody an explanation for this? I set the backgroundColor in Interface Builder. Thanks!
Sorry for this monologue. :)
The thing is that the UIView's layer apparently draws the background, which is independent from drawRect:. This is why you can't get rid of the background by overriding drawRect:.
You can either override - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx and make the layer draw whatever you want, or you override - (void) setBackgroundColor:(UIColor *)newColor and don't assign newColor to backgroundColor, but to your own ivar, like myBackgroundColor. You can then use myBackgroundColor in drawRect; to draw the background however you like.
Overriding setBackgroundColor:
Define an instance variable to hold your background color, e.g. myBackgroundColor. In your init methods, set the real background color to be the clearColor:
- (id) initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super init...])) {
[super setBackgroundColor:[UIColor clearColor]];
}
return self;
}
Override:
- (void) setBackgroundColor:(UIColor *)newColor
{
if (newColor != myBackgroundColor) {
[myBackgroundColor release];
myBackgroundColor = [newColor retain];
}
}
Then use myBackgroundColor in your drawRect: method. This way you can use the color assigned from Interface Builder (or Xcode4) in your code.
Here's an easier fix:
self.backgroundColor = [UIColor clearColor];
I hesitate to suggest this because I don't want to offend you, but have you set opaque to NO?
This is easily reproducible:
Create a new View-Based iPhone Application. Create an UIView subclass and leave the drawRect: method completely empty. In Interface Builder, drag a UIView into the main view, assign it a background color and set the class of the view to your subclass. Save, build and run, and see, the view shows the background color.
I have circumvented this behaviour by overriding setBackgroundColor: and assigning the color to my own ivar, always leaving the backgroundColor property nil. This works, however I'm still wondering why this works this way.
You should set
clearsContextBeforeDrawing = NO
It's that simple.
From the UIView Reference Docs
Oops. I misunderstood the question. The OP (original poster) wants their custom control to support the standard "backgroundColor" property (eg, set by the gui designer), but does NOT want the system to paint that color. The OP wants to paint the bg himself so that he can round the corners.
In that case, the post about overriding the layer level drawing is correct.
The solution I posted will prevent the UI system from clearing your buffer before drawing. When you have no bg defined, if clearsContextBeforeDrawing is set, the iOS will clear your view's buffer, setting all pixels to transparent black. Doing this for a full-screen view takes about 5ms in an iPad3, so it's not free (pushing that 2048x1536 pixels never is). For comparison drawing a full-screen bitmap (using kCGBlendModeCopy to force blitting) takes ~25ms (using quartz, not GPU).
While it is possible to override the code that copies the background colour into the view's layer, I'm not a fan of this approach. I believe it can cause issues with views intended to be opaque. My suggestion is to create a custom colour variable like this:
#IBInspectable var foregroundColor: UIColor = .black {
didSet { setNeedsDisplay() }
}
Now, set your view's backgroundColor to clear and use foregroundColor in your drawing code in its place. The code above takes care of updating it when the value changes, and also exposes it to Interface Builder if required.
Another alternative is to use your view's tintColor instead, and to ensure that it updates correctly when tint colour changes. You may be reserving tint colour for other uses though, so this is not necessarily ideal. If you use tintColor, don't forget to include the following:
override func tintColorDidChange() {
setNeedsDisplay()
}