How to always get rounded corners (corner-radius) on a view that has dynamic height? - ios

I'm rather new to iOS programming.
I was wondering what is the proper way to achieve permanently rounded corners (via the attribute view.layer.cornerRadius) for a view that has dynamic height.
In Android, we would just set the cornerRadius to an absurdly high number like 1000. This would result in the view always having rounded corners regardless of how tall or short it was.
Unfortunately, when I tried to do the same thing in iOS, I realized that an overly large value for cornerRadius results in the view being drawn in a distorted way - or straight up just disappearing from the layout altogether.
Anyone have any insights into this problem? Thanks.

Easy to achieve this with RxSwift by observing Key Path.
extension UIView{
func cornerHalf(){
clipsToBounds = true
rx.observe(CGRect.self, #keyPath(UIView.bounds))
.subscribe(onNext: { _ in
self.layer.cornerRadius = self.bounds.width * 0.5
}).disposed(by: rx.disposeBag)
}
}
Done in init method, the code seems simpler by declaration , instead of being distributed two parts. Especially, the logic is widely used in the project.
Call like this:
let update: UIButton = {
let btn = UIButton()
// more config
btn.cornerHalf()
return btn
}()

Related

Swift -How to change the color of the curve on a UISwitch control

How can I change the color of the curve that follows a UISwitch control? I tried changing the borderColor but that's tied to the frame.
This is a sample photo from another question. I want to toggle the color of the silver curve in this photo (not the round button and not the orange background)
I tried these 2 properties but the .layer.borderWidth = 2.0 and .layer.cornerRadius = 15 changes the frame and not the curve
When it's on I want the curve to be .clear and when it's off I want it to be .red
lazy var switchControl: UISwitch = {
let switchControl = UISwitch()
switchControl.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged)
}()
#objc func switchValueDidChange(_ sender: UISwitch) {
if (sender.isOn == true) {
sender.layer.borderColor = UIColor.clear.cgColor // this doesn't work
} else {
sender.layer.borderColor = UIColor.red.cgColor // this doesn't work
}
}
Yes as iChirag said this can not be done as of now using the built-in UISwitch.
As to why it's not possible, it's hard to say with certainty. My guess would be that it's because Apple encourages using a limited color palette in their Human Interface Guidelines. The main goal of using colors in an iOS app is basically to communicate information, help the user differentiate separate parts of the UI at a glance or to call attention to user actions. Maybe they think that setting a custom color for the curve of a UISwitch is outside of these purposes.
With that said, it's fairly easy to build your own custom UISwitch if you need this appearance. Here is a tutorial to get you started, hope this helps:
Make custom UISwitch (Part 1)
Change Tint color of UISwitch
mSwitch.tintColor = offColor// Custom color
I believed this doesn't possible without some hacking tips of SDK. But I am proposing to create your own such control using an image.

Swift circular corners doesn't work properly on different screen sizes

I'm trying to achieve smooth round corners on imageviews. Although it's working well on non-plus devices, i can not reach my goal at plus screen devices. Incidentally, i recognize, it's not even work well on the devices that font size enlarged. I applied following code that can be found every topic.
mergedImage.image = lastImage
mergedImage.layer.masksToBounds = false
mergedImage.layer.cornerRadius = mergedImage.frame.size.height / 2
mergedImage.clipsToBounds = true
And it's results like pictures below.
It looks like the black shape changes size. This can happen when using AutoLayout for example. If that is the case, you need to calculate the corner radius each time the frame changes.
I think the best way to do this is by subclassing UIView to create a "black shape view", and then overriding its layoutSubviews method:
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height / 2
}
If you don't have a subclass, you can for example do this in UIViewController.viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
mergedImage.layer.cornerRadius = bounds.height / 2
}

Gradient layer not in the right place

I have the following code as follows:
playView.layer.cornerRadius = 16
let gradient1 = CAGradientLayer()
gradient1.frame = playView.frame
gradient1.cornerRadius = 16
if #available(iOS 10.0, *) {
// set P3 colour
} else {
// set sRGB colour
}
gradient1.startPoint = CGPoint(x: 0, y: 0)
gradient1.endPoint = CGPoint(x: 1, y: 1)
playView.layer.insertSublayer(gradient1, at: 0)
On the 3rd line of the block of code above, I set the frame of the gradient equal to the frame I want it to fill.
When I run the app on different devices, the gradient layer will only fill the correct area if the device the app is being run on is the one selected in the Interface Builder.
I currently have the code in viewDidLoad(), and so the issue can be solved by moving the code to viewDidAppear(), but then when the app is loaded, there will be a slight delay before the gradient appears, not giving a smooth look and feel.
Is there another method I can put the code in, so that the gradient shows in the correct place, whilst at the same time being there as soon as the user sees the screen? Or alternatively, a way to make the gradient fill the view, whilst still keeping the code in viewDidLoad()?
EDIT: viewWillAppear() does not work, nor does viewWillLayoutSubviews(). Surely there must be away to solve this?
You can put inside this block:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
}
viewDidAppear() works. This screen is the root controller - I don't know if that makes a difference or not, but there is no visible delay on applying the gradient backgrounds.
I would be interested if anyone could explain this? I have a bar chart in another part of the app and in viewDidAppear() there is code to complete the bar chart, however there is a delay in it being filled in.
Change the layer.frame property inside the viewDidLayoutSubviews
method. This is to make sure that the subview (playView) has already a proper frame.

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.)

How to align data points in shinobichart based on bars frame in iOS

I'm developing an iOS application and using shinobichart for iOS to display data. But I'm stuck with positioning the datapoints outside or inside the bar based on the bar frame.
I'm stuck in positioning the data point labels inside the bar or outside the bar based on bar frame. If bar is big enough to accommodate the data point I need to position the data point inside bar or else outside the bar as in the below image
In - (void)sChart:(ShinobiChart *)chart alterDataPointLabel:(SChartDataPointLabel *)label forDataPoint:(SChartDataPoint *)dataPoint inSeries:(SChartSeries *)series we need to update data points if we wish as per shinobi documentation. But what logic can help to solve such an alignment of data points.
Also I tried to with the following official shinobi example to align data point aside https://www.shinobicontrols.com/blog/customizable-datapoint-labels
But unable to modify for the requirement.
Please help to solve this riddle.
The best way for you to achieve this is to use the alterDataPointLabel method as you have mentioned. I would do something like this:
func sChart(chart: ShinobiChart!, alterDataPointLabel label: SChartDataPointLabel!, forDataPoint dataPoint: SChartDataPoint!, inSeries series: SChartSeries!) {
if Int(label.text!) > [YOUR CUT OFF VALUE]
{
label.frame.origin.y += 15
label.textColor = UIColor.whiteColor()
}
else
{
label.frame.origin.y -= 15
label.textColor = UIColor.blackColor()
}
}
This works best if you know the values your chart is going to be displayed. However as a more generic way of achieving it I would recommend comparing the y value of the data label's origin against y value of the chart canvas. Then for example you could set the it so it appears within the column if it the series was 90% of the canvas?

Resources