Update subview's corner radius when Cell's size change - objective-c

I have a VC (MainVC), inside hold a property UICollectionView. This CollectionView has a custom layout (UICollectionViewFlowLayout).
I also have a setting, which can change some of this custom layout's property (number of items per page).
Now, inside the collectionViewCell, I have an image, which desired to have rounded rect. So I use this:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
self.imageView.layer.cornerRadius = self.imageView.frame.size.width / 2;
}
However, when I apply the setting, change the custom flow layout, the cell doesn't re-draw and the drawRect: never got called => Result: not a rounded rect border for the imageView.
What should I do to apply new layout (resize the cell with custom layout) and remain rounded rect for the cell's imageView?

Related

NSScrollView clips document view instead of allowing it to scroll

I have custom, layer-backed NSView as the document view of a NSScrollView. The scroll view is inside a NSSplitView, which itself uses constraints to ensure that it fills the entire window.
The bounds of the custom view are never explicitly set. At one point, however, the bounds of the backing layer are set to encompass every child layer. Setting the bounds explicitly doesn't help.
When I resize the window, the scroll view clips my custom view and I find myself unable to scroll:
When I debug my view, I find that resizing the window also resizes the custom view's frame. I don't really know if this is normal. My autoresize mask is .ViewMinYMargin, and translatesAutoresizingMaskIntoConstraints if off (but it doesn't appear to change anything if I turn it on).
Considering that there are basically no instructions on what to do to make scrolling work in the Apple guide, I must be missing something fairly simple. Does anyone have any idea?
This happened because of a misunderstanding of autolayout constraints. Each edge of the custom view was constrained to be at a distance of 0 to the edge of the clip view. This effectively set the size of the view to whatever the size of the clip view was, preventing scrolling because it made it look like the clipped size was the custom view's natural size.
The solution was to constrain only the left, top and right edges, and leave the bottom edge free. Instead, I made a height constraint on the custom view, and I programmatically set its constant value to the size of my view's contents (which is dynamically generated).
Sample code for LayerBackedView:
class LayerBackedView: NSView {
required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
}
override var flipped: Bool {
// hug top of scroll view if not high enough to fill it entirely
return true
}
private var heightConstraint: NSLayoutConstraint {
let firstConstraint = constraints[0] as! NSLayoutConstraint
assert(firstConstraint.firstAttribute == .Height)
return firstConstraint
}
override func awakeFromNib() {
let red = CALayer()
red.anchorPoint = CGPointMake(0, 0)
red.backgroundColor = CGColorCreateGenericRGB(1, 0, 0, 1)
red.borderWidth = 2
red.borderColor = CGColorCreateGenericGray(0, 1)
layer!.addSublayer(red)
// set height constraint to whatever you need it to be
let desiredHeight: CGFloat = 200
heightConstraint.constant = desiredHeight
red.frame = CGRectMake(0, 0, bounds.width, desiredHeight)
}
}
Constraints on the LayerBackedView (the height value doesn't matter because we change it programmatically, the constraint just needs to exist):
Size constraints are added to the view itself, but spacing constraints are added to the superview. This means that out of the four constraints, the height constraint will be the only one inside the view (and this is why we can do constraints[0] as! NSLayoutConstraint). If this is not your case, you can make an #IBOutlet to it instead.

Scrollview vertical only with fixed width in Autolayout

In swift how can I set the width of the scroll view to be the width of the screen size?
I tried setting right leading and trailing bounds, but my aspectscalefill image keeps stretching the entire frame.
I am trying to force the view to be the width of the screen and allow vertical scrolling only.
If you want to make a scroll view with auto layout try this. No code at all so you will have to drag things out of the library to the right of Xcode.
In your view controller drag and place the scroll view size it to whatever you want but it looks like you want to make it the size of the screen. Pin all edges to the edges of the view controller. Pin trailing, leading, top, and bottom.
Now, instead of placing your items in the scroll view, place another view in the scroll view. With this new view you will place all of your items. You will most likely have to move the view up or down to place them all and resize the view. You can then place whatever constraints you want on your items. When all items are in their place, set the frame of the new view back to x = 0 and y = 0.
You will then place constraints as follows. Select the new view and pin to top, bottom, trailing, and leading and then center in container. This will make a constraint that is vertical with some negative number. In the storyboard outline select this constraint and set it to zero.
You will now be able to scroll vertically. Let me know if you have any questions.
I created a single view application, and added a UIScrollView in the storyboard with four constraints (top, left, right, bottom). And I add the following code in the viewDidLoad(). Everything works just fine. My image is only scrolling vertically, but not horizontally. No additional settings are needed.
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Just as an example, I make a size with same with of the view
// and twice the height of the view.
let size = CGSizeMake(
self.view.frame.size.width,
self.view.frame.size.height * 2);
// Load an image and make a image view
let image = UIImage(named: "im.jpg")!
let imageView = UIImageView(image: image)
imageView.frame = CGRect(
origin: CGPoint(x: 0, y: 0),
size: size);
imageView.contentMode = UIViewContentMode.ScaleToFill;
// Add the image view to scroll view
scrollView.addSubview(imageView)
scrollView.contentSize = size;
}
Your problem i caused by the fact that you add your elements to the scrollView from the storyBoard.
In the storyBoard you need:
1) Add the scrollView, if you need to be in the width of the screen then set it's trailing and leading spaces to superView to 0
2) uncheck the "allow horizontal scrolling" in the attribute inspector
In your code
1) set the scrollView's contentSize to what ever size you want it to be.
1.1) in your code you need to set the size of the contentView like so
scroller.contentSize = CGSizeMake(your width, your height);
This is a very important step, the scrollView will not be able to scroll if you don't set the size. Think about the scrollView as the window and the contentView as the view. If the contentView if the same size as the scrollView then the scrollView can't scroll anywhere (all of the contentView fits into the scrollView) in order to create the scrolling you need to make the contentView bigger then the scrollView itself
2) start adding your elements to the scrollView's contentView by calling
[scroller addSubView:<your view>];
this will add your views to the scrollView's contentView and will be

How to customize a NSSlider

I'm trying to implement a custom slider in Cocoa with 5 values. See my demo project, which can be downloaded here: http://s000.tinyupload.com/index.php?file_id=07311576247413689572.
I've subclassed the NSSliderCell and implemented methods like drawKnob:(NSRect)knobRect and drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped etc.
I'm facing some issues:
I'm not able to position the knob correctly regarding to the background image. I know that I'm able to change the knob's frame, and I've tried doing some calculation to position the knob correctly, but I'm not able to make it work for my custom slider. Could someone please help me with this?
The height of my custom slider background is 41px. In the drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped I change the height of the frame to 41px as well, but the entire background is not visible. Why?
I've noticed that the included images (the background and knob) are flipped vertically. Why? Note that the border top is darker in the background compared to the bottom, but this is reversed when I draw the background.
I found a mistake in your calculation of the x position of the knob rectangle: You used the height of the image where you should have used the width.
The cell drawing is being clipped to the frame of the control. Maybe you could expand the control frame when your cell awakes.
You need to use the NSImage method drawInRect:fromRect:operation:fraction:respectFlipped:hints:, and pass YES for the respectFlipped: parameter. Apple's controls generally do use flipped coordinates.
Added: Expanding the frame in awakeFromNib doesn't seem to work, the frame gets set back. Here's something that does work. Instead of overriding drawBarInside:flipped:, add this override:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
NSRect controlFrame = [controlView frame];
float bgHeight = self.backgroundImage.size.height;
if (controlFrame.size.height < bgHeight)
{
controlFrame.size.height = bgHeight;
[controlView setFrame: controlFrame];
}
[self.backgroundImage
drawInRect: [controlView bounds]
fromRect: NSZeroRect
operation: NSCompositeSourceOver
fraction: 1.0
respectFlipped: YES
hints: NULL];
[self drawKnob];
}

How to keep UIView at relative distance from other after resizing?

I have five UIView on a UIScrollView. All of them with the same width. Each view has other subviews that resize its height according to the content assigned, thus making the parent UIView and the UIScrollView resizable as well. I am trying to keep the 5 UIView separated from each other at a certain "Padding" distance even after resizing. What I do right now is set the position of the origin.y and the height of each UIView when layoutSubviews is called. Is there an easier way to do this?
I have tried to set their position on creation like: CGRectMake(0, aboveView.frame.origin.y + aboveView.frame.size.height + Padding, width, 0) and setting its autoresizingMask to UIViewAutoresizingMaskTopMargin. Hoping that when I call sizeToFit on the main UIView, all the UView will set their positions relative to the view above them.
Overriding layoutSubviews is the right way to do this. UIKit doesn't have any built-in layout management that can do it for you.
However, you might not realize that UIScrollView sends itself layoutSubviews each time it scrolls - on every frame of the scrolling. That may be a lot more often than you need! You don't want to do a lot of work in a UIScrollView's layoutSubviews if you can avoid it.
To avoid doing extra layout, I suggest you set up your view hierarchy like this:
UIScrollView
ContainerView with layoutSubviews method
content view 1
content view 2
content view 3
content view 4
content view 5
Use a standard UIScrollView. Give it one subview, which is a custom UIView subclass (I called it ContainerView in my example). The ContainerView has your five content views as its subviews.
When you assign new content to one of your five content views, send sizeToFit to that content view. If the view's size changes, UIKit should automatically send layoutSubviews to its superview - the ContainerView. The ContainerView's layoutSubviews method adjusts the position of its subviews to maintain the padding between them, and then sets the contentSize of its parent - the UIScrollView.
- (void)layoutSubviews {
CGRect myFrame = CGRectZero;
for (UIView *subview in self.subviews) {
CGRect frame = subview.frame;
if (myFrame.size.height > 0) {
frame.origin.y = myBounds.size.height + Padding;
subview.frame = frame;
}
myFrame = CGRectUnion(myFrame, frame);
}
self.frame = myFrame;
UIScrollView *scrollView = self.superview;
scrollView.contentSize = myFrame.size;
}
This way, you don't do any extra work just because the scroll view scrolled. You only lay out your content views when the content actually changes.

How do you determine the size/frame of a custom UINavigationItem.titleView?

After creating a custom view and assigning it to the navigationItem.titleView property it is displayed like this
with the custom view filling the space between the two buttons. Therefore, the custom view is not centered on the navigation bar. How do I determine the frame of the view in the .titleView property? I want to center some text in the navigation bar, say under the time stamp.
If you really want to get titleView's frame (in your top-level view's coordinate space), you can do this:
[self.navBar layoutIfNeeded];
CGRect titleViewFrameInTopLevelViewSpace = [self.navigationItem.titleView
convertRect:self.navigationItem.titleView.bounds
toView:self.view];
You need to do layoutIfNeeded if you have just assigned titleView, because by default the navigation bar won't lay out its subviews until the next pass through the run loop.
That said, the titleView will be centered automatically, if it fits. I think you are setting the frame (or bounds) of your custom view too large. I tested this two ways:
I set up the titleView directly in the XIB. I simply dragged a View from the Object library onto the center of the navigation bar:
It sized the view to 128x33 automatically. The resize handles let me adjust the size. It stays centered until it overlaps the Categorize button. Then it shifts left.
I set the titleView property in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 33)];
customView.backgroundColor = [UIColor greenColor];
self.navItem.titleView = customView;
}
The result looks like this:
You could get the width of the leftBarButtonItem and the rightBarButtonItem after you've set them, and then use that to determine how to centre within the view you supply to titleView. That might do what you want?