I can not get this UIView round - ios

This is the final result of the code below:
This is my code:
view.layer.cornerRadius = 0.5 * view.bounds.size.width
view.layer.masksToBounds = true
view.clipsToBounds = true
view is the red UIView. This code is placed in viewWillLayoutSubviews and viewDidLayoutSubviews (not together, but I both tested them).
Besides this SO is not clear about using masksToBounds OR clipsToBounds. I am not sure what to use and when to use one of them above. I also tested it apart.
The green UIView has also clipsToBounds active and rounded corners, but if I remove them both, I still get the same effect... I hope someone can help. This only occurs when I place a UIView, inside another UIView. If the UIView does not has a parent, it works (I get the UIView rounded).
Edit: GitHub link: https://github.com/Jasperav/tests

The problem lies with when the auto-layout engine actually finishes all the layout calculations.
If you move your code to viewDidAppear you will see properly "rounded" corners.
This is a very common case for using a custom view though. In fact, it's easily used as an "IBDesignable" class, so you can see it rendered in Interface Builder.
Create a new file named "RoundedView.swift" with this code:
import UIKit
#IBDesignable
class RoundView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = 0.5 * self.bounds.size.height
}
}
Then assign that class to the View you want to be round. Xcode will build your new class, and it will show up in Interface Builder like so:

Related

iOS Swift round views inside cell show incorrectly

Evening, I have a calendar collection.
The cells have some rounded views shown incorrectly at the first time, but when they are reloaded they are shown correctly.
I know that the issue is that at the first time the cell doesn't know the right size of the frame.
What I've tried:
1- call the round function inside layoutSubviews(): only the right side is rounded correctly
2 - call the round function inside the cellWillLayout: nothing changes
This is the rounding function:
func makeRound() {
print("rounding")
currentDayView.layer.cornerRadius = currentDayView.frame.height/2
currentDayView.layer.masksToBounds = true
currentDayView.clipsToBounds = true
selectedDayView.layer.cornerRadius = selectedDayView.frame.height/2
selectedDayView.layer.masksToBounds = true
selectedDayView.clipsToBounds = true
}
Any suggestion?
The best place to do corner rounding is either in each view's layoutSubviews or (for example, if you haven't sub-classed them) put it in your view controllers viewDidLayoutSubviews.
Each view in your case is the layoutSubviews of currentDayView and selectedDayView.
You need to override layoutSubviews method, then call your method inside it:
override func layoutSubviews() {
super.layoutSubviews()
self.makeRound()
}

Swift: Rounded corners appear different upon first appearance and reappearing

class ApplyCorners: UIButton {
override func didMoveToWindow() {
self.layer.cornerRadius = self.frame.height / 2
}
}
I apply this class to the buttons in my application and it is working great, but when I apply it to a button used in every cell in a table view the button corners are not round upon entering the view, but if I click one of the buttons I get segued to another view. If I then segue back the corners are "fixed" / round.
The green is the button when returning and the red is upon first entering the view.
Anyone know how to fix this?
I'd suggest layoutSubviews, which captures whenever the frame of the button changes:
class ApplyCorners: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = frame.height / 2
}
}
This takes care of both the original appearance and any subsequent appearance. It also avoids all sorts of problems related to not only whether the frame was known when the view appeared, but also if you do anything that might change the size of the button (e.g. anything related to constraints, rotation events, etc.).
This sort of thing is likely to be a timing problem. Consider the phrase self.frame.height. At the time didMoveToWindow is called, we may not yet know our frame. If you are going to call a method that depends upon layout, do so when layout has actually occurred.
Gonna propose another alternative: listen to any bounds changes. This avoids the problem of wondering "is my frame set yet when this is called?"
class ApplyCorners: UIButton {
override var bounds: CGRect {
didSet {
layer.cornerRadius = bounds.height / 2
}
}
}
Edited frame to bounds because as #Rob points out, listening for frame changes will cause you to miss the initial load sometimes.
Putting your code in didMoveToWindow() does not make sense to me. I'd suggest implementing layoutSubviews() instead. That method gets called any time a view object's layout changes, so it should update if you resize your view.
(Changed my suggestion based on comments from TNguyen and and Rob.)

Insert sublayer behind UIImageView

i'm trying to add a sublayer behind the imageView however the issue is that since it is using constraints it can't seem to figure out the position and just places sublayer in left corner? i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
viewDidLoad
//GroupProfile ImageView
imageGroupProfile = UIImageView(frame: CGRect.zero)
imageGroupProfile.backgroundColor = UIColor.white
imageGroupProfile.clipsToBounds = true
imageGroupProfile.layer.cornerRadius = 50
self.view.addSubview(imageGroupProfile)
imageGroupProfile.snp.makeConstraints { (make) -> Void in
make.height.equalTo(100)
make.width.equalTo(100)
make.centerX.equalTo(self.view.snp.centerX)
make.centerY.equalTo(self.view.snp.centerY).offset(-40)
}
let pulseEffect = LFTPulseAnimation(repeatCount: Float.infinity, radius:160, position:imageGroupProfile.center)
self.view.layer.insertSublayer(pulseEffect, below: imageGroupProfile.layer)
i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
Nevertheless that is the way to do it. Just add a Bool property so that your implementation of viewDidLayoutSubViews inserts the layer only once:
var didLayout = false
override func viewDidLayoutSubviews() {
if !didLayout {
didLayout = true
// lay out that layer here
}
}
The reason is that you don't have the needed dimensions until after viewDidLayoutSubviews tells you that (wait for it) your view has been laid out! But, as you rightly say, it can be called many times subsequently, so you also add the condition so that your code runs just once, namely the first time viewDidLayoutSubviews is called.

drawRect not called when subclassing UIView and adding it in the interface builder

I am trying to subclass UIView. Code like so:
import UIKit
class CustomView: UIView {
override func drawRect(rect: CGRect) {
// Drawing code
let circlePath = UIBezierPath(ovalInRect: rect)
UIColor.blueColor().setFill()
circlePath.fill()
}
}
I have dragged a UIView onto a UIViewController and set the class to CustomView however the circle doesn't appear (I have set the constraints correctly). Any ideas what I might be doing wrong here? I'm not trying to preview it. Just want to see it when I run the app. Seems I am getting an error in the console too: Unknown class _TtC9MyProject15CustomView in Interface Builder file.
It is likely that the Target Membership for your CustomView.swift file is not set for your app.
In the Project Navigator on the far left, select your file CustomView.swift. Then in the File Navigator on the far right under Target Membership, make sure the box to the left of your app name is checked.

Live Xcode's Interface Builder UIView subclass (IB_DESIGNABLE) without drawRect:

Is it possible to create a UIView subclass that renders live in Xcode (by adding the IB_DESIGNABLE attribute as explained here) but doesn't have a custom drawRect: method?
We have a custom UIView subclass that uses some CAShapeLayers which are added to self.layer for drawing (hence, there's no need to override drawRect:). This class works fine on the App, but won't render on Xcode.
If we replicate the code in drawRect: it works, but we'd prefer to keep the drawing to happen automatically on the layers.
Can this be done?
I also tried doing
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
[self.myLayer1 renderInContext:currentContext];
CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
[self.myLayer2 renderInContext:currentContext];
}
which seems to work on the device but not on Xcode's IB.
You can preview UIView subclasses in IB (with the IB_DESIGNABLE macro) even if the drawRect: isn't overridden.
I added your code in XCode 6.1 and added a OEProgressIndicator into a xib file. Then I debugged it (using menu Editor / Debug Selected View ) by setting a breakpoint in your commonProgressIndicatorInit selector.
Here's why you don't see anything in the preview with your current code: when the commonProgressIndicatorInit is invoked (from the initWithFrame:(CGRect)frame constructor), the frame is equal to CGRectZero (x:0 y:0 width:0 height:0) so that your center variable is actually equal to (0, 0) and radius is -1.
On the device, depending on the way the class is used, you may be directly invoked with the proper frame, that's why it may work on the device but not in IB.
To fix this, I would implement the layoutSubviews selector (override it from UIView) to organise properly the sublayers. This selector is going to be invoked when the frame is going to change from CGRectZero to the proper values set in Interface Builder.
I've been using the method - (void)prepareForInterfaceBuilder in order to tell IB to live render a view.
See here: Creating a Live View of a Custom Object
Also, you guys are right that this feature is also available for Objective-C.
You don't necessarily need to use drawRect, you can try using - (void)layoutSubviews, it seems to work. The problem with leaving code in places like - (void)layoutSubviews just for the sake of live rendering is that it may be less performant, etc (for instance you can do a lot of stuff in - (void)awakeFromNib, but that method does not get called from Live Rendering, so just make sure that you do all your set up in - (void)prepareForInterfaceBuilder as well.
Without seeing all your code, it's hard to see what the source of the problem is, but to answer your question, yes, you can use IBInspectable / IBDesignable without needing to implement any other specific method. I have done this for a view that uses many layers and does not do any drawing (uses the nested layers for that).
For a quick test example snippet with rounded corners:
#IBDesignable
class MyView : UIView {
#IBInspectable var cornerRadius:CGFloat {
get { return self.layer.cornerRadius }
set { self.layer.cornerRadius = newValue }
}
#IBInspectable var borderWidth:CGFloat {
get { return self.layer.borderWidth }
set { self.layer.borderWidth = newValue }
}
#IBInspectable var borderColor:UIColor {
get { return UIColor(CGColor: self.layer.borderColor) }
set { self.layer.borderColor = newValue.CGColor }
}
}
For a simple example that does gradients, see this post.
For an explanation on how to debug the live views, refer to WWDC ยง411 at about the 22 minute mark.
The only limitation that I have seen so far is that you can add inspectable properties in class extensions, but they only render properly on the device.

Resources