swift - change color of top UIToolbar border - swift

I'm trying to change the color of the top border on a UIToolbar.
I tried:
layer.borderWidth = 1
layer.borderColor = UIColor(r: 250, g: 250, b: 250).CGColor
That didn't work.
Suggestions? Thank you

I've done it using this code:
class ViewController: UIViewController {
#IBOutlet weak var toolBar: UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
// the layer with the width of the device
// and height of 0.5 px
let topBorder = CALayer()
topBorder.frame = CGRectMake(0, 0, view.frame.size.width, 0.5)
topBorder.backgroundColor = UIColor.redColor().CGColor
// adding the layer to the top
// of the toolBar
toolBar.layer.addSublayer(topBorder)
}
}
Result:
Source: https://blog.adamcooke.io/set-the-top-border-colour-of-a-uinavbar-d9035c6b4fdb#.f37molpsj

If you don't like its color, and as it isn't possible to change it, fastest hack is to get rid of that hairline - It is not actually a border, it's a shadow.
toolbar.clipsToBounds = true
OR:
toolbar.layer.shadowOpacity = 0
Or maybe you are able to change its shadow image:
UITabbar.appearance().shadowImage = UIImage.colorForNavBar(.red)

UIColor works between 0 and 1 so for example:
If you want 250 as your red value you need to do 250/255. I had this same problem and found that just by adding the /255 after the value the color works as expected.
The /255 creates the value in the proper range. You could also do the division and put in the decimal approximation.
If you need any more help or clarification please let me know.

You need to set RGB colours divided by 255.0 to get a 0 to 1 colour.
For example:
layer.borderWidth = 1
layer.borderColor = UIColor(r: 250/255, g: 250/255, b: 250/255).CGColor

You can use:
- (void)setToolbarLineWith:(UIColor *)color
{
for(UIView *v in self.navigationController.toolbar.subviews){
if([NSStringFromClass([v class]) isEqualToString:#"_UIBarBackground"])
{
for(UIView *w in v.subviews){
if([NSStringFromClass([w class]) isEqualToString:#"UIImageView"])
{
[w setBackgroundColor:color];
}
}
}
}
}
and run with:
[self setToolbarLineWith:[UIColor redColor]];

That's actually a shadow image, so just create some 1 pixel image with necessary color and use
[toolbar setShadowImage:<your image> forToolbarPosition:UIBarPositionAny];

Related

How to give an imageView in swift3.0.1 shadow at the same time with rounded corners

I want to give an imageView a shadow at the same time with rounded corners,but I failed.
Here is my solution
Basic idea :
Use an Extra view (say AView) as super view of image view (to those views on which you are willing to have shado) and assign that view class to DGShadoView
Pin Image view to AView (that super view)from left, right, top and bottom with constant 5
Set back ground color of the AView to clear color from storybosrd's Property inspector this is important
Inside idea: Here we are using a Bezier path on the Aview nearly on border and setting all rounded corner properties and shadow properties to that path and we are placing our target image view lie with in that path bound
#IBDesignable
class DGShadoView:UIView {
override func draw(_ rect: CGRect) {
self.rect = rect
decorate(rect: self.rect)
}
func decorate(rect:CGRect) {
//self.backgroundColor = UIColor.clear
//IMPORTANT: dont forgot to set bg color of your view to clear color from story board's property inspector
let ref = UIGraphicsGetCurrentContext()
let contentRect = rect.insetBy(dx: 5, dy: 5);
/*create the rounded oath and fill it*/
let roundedPath = UIBezierPath(roundedRect: contentRect, cornerRadius: 5)
ref!.setFillColor("your color for background".cgColor)
ref!.setShadow(offset: CGSize(width:0,height:0), blur: 5, color: "your color for shado".cgColor)
roundedPath.fill()
/*draw a subtle white line at the top of view*/
roundedPath.addClip()
ref!.setStrokeColor(UIColor.red.cgColor)
ref!.setBlendMode(CGBlendMode.overlay)
ref!.move(to: CGPoint(x:contentRect.minX,y:contentRect.minY+0.5))
ref!.addLine(to: CGPoint(x:contentRect.maxX,y:contentRect.minY+0.5))
}
}
Update
Extension Approach
There is another Approach. Just Make a class with empty and paste Following UIImageView Extension code, Assign this subclass to that ImageView on which you shadow.
import UIKit
class DGShadowView: UIImageView {
#IBInspectable var intensity:Float = 0.2{
didSet{
setShadow()
}
}
override func layoutSubviews()
{
super.layoutSubviews()
setShadow()
}
func setShadow(){
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 0.3)
layer.shadowOpacity = intensity
layer.shadowPath = shadowPath.cgPath
}
}
The solution is to create two separate views. One for the shadow and one for the image itself. On the imageView you clipToBounds the layer so that the corner radius is properly added.
Put the imageView on top of the shadowView and you've got your solution!

Borders not covering background

I've got a UILabel is using a border the same color as a background which it is half obscuring, to create a nice visual effect. However the problem is that there is still a tiny, yet noticeable, sliver of the label's background color on the OUTSIDE of the border.
The border is not covering the whole label!
Changing the border width doesn't change anything either, sadly.
Here's a picture of what's going on, enlarged so you can see it:
And my code follows:
iconLbl.frame = CGRectMake(theWidth/2-20, bottomView.frame.minY-20, 40, 40)
iconLbl.font = UIFont.fontAwesomeOfSize(23)
iconLbl.text = String.fontAwesomeIconWithName(.Info)
iconLbl.layer.masksToBounds = true
iconLbl.layer.cornerRadius = iconLbl.frame.size.width/2
iconLbl.layer.borderWidth = 5
iconLbl.layer.borderColor = topBackgroundColor.CGColor
iconLbl.backgroundColor = UIColor.cyanColor()
iconLbl.textColor = UIColor.whiteColor()
Is there something I'm missing?
Or am I going to have to figure out another to achieve this effect?
Thanks!
EDIT:
List of things I've tried so far!
Changing layer.borderWidth
Fussing around with clipsToBounds/MasksToBounds
Playing around the the layer.frame
Playing around with an integral frame
EDIT 2:
No fix was found! I used a workaround by extending this method on to my UIViewController
func makeFakeBorder(inputView:UIView,width:CGFloat,color:UIColor) -> UIView {
let fakeBorder = UIView()
fakeBorder.frame = CGRectMake(inputView.frame.origin.x-width, inputView.frame.origin.y-width, inputView.frame.size.width+width*2, inputView.frame.size.height+width*2)
fakeBorder.backgroundColor = color
fakeBorder.clipsToBounds = true
fakeBorder.layer.cornerRadius = fakeBorder.frame.size.width/2
fakeBorder.addSubview(inputView)
inputView.center = CGPointMake(fakeBorder.frame.size.width/2, fakeBorder.frame.size.height/2)
return fakeBorder
}
I believe this is the way a border is drawn to a layer in iOS. In the document it says:
When this value is greater than 0.0, the layer draws a border using the current borderColor value. The border is drawn inset from the receiver’s bounds by the value specified in this property. It is composited above the receiver’s contents and sublayers and includes the effects of the cornerRadius property.
One way to fix this is to apply a mask to a view's layer, but I found out that even if so we still can see a teeny tiny line around the view when doing snapshot tests. So to fix it more, I put this code to layoutSubviews
class MyView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
let maskInset: CGFloat = 1
// Extends the layer's frame.
layer.frame = layer.frame.inset(dx: -maskInset, dy: -maskInset)
// Increase the border width
layer.borderWidth = layer.borderWidth + maskInset
layer.cornerRadius = bounds.height / 2
layer.maskToBounds = true
// Create a circle shape layer with true bounds.
let mask = CAShapeLayer()
mask.path = UIBezierPath(ovalIn: bounds.inset(dx: maskInset, dy: maskInset)).cgPath
layer.mask = mask
}
}
CALayer's mask

Swift: Different Sizes of two UIViews

I have a problem respective to UIView and UIView subclasses.
In my storyboard I add a UIView (width:368 height:552, center horizontally and vertically).
I have connected this UIView to my ViewController:
#IBOutlet weak var mapViewOutlet: UIView!
The I want to create a object of my own subclass of UIView:
var bounds:CGRect = CGRectMake(0, 0, mapViewOutlet.frame.size.width, mapViewOutlet.frame.size.height)
self.roomView = RoomView(frame: bounds, image: testMap)
The first parameter of the init method is bounds. There I give the size of mapViewOutlet to the subclass. The image parameter is the green image (you can see this in the attached image).
But now the size of the green image does not fit the size of the gray mapViewOutlet.
The green UiView is generated as follows:
let testFrame : CGRect = CGRectMake(0,0,frame.size.width ,frame.size.height)
var testView : UIView = UIView(frame: testFrame)
testView.backgroundColor = UIColor(red: 0.5, green: 4, blue: 0.5, alpha: 1.0)
testView.alpha=0.5
self.addSubview(testView)
Does anybody know how they do not fit?
Thanks.
Now I know the problem.
The sizes are wrong only in the viewDidLoad().
The following code gives the correct values of the width and height.
override func viewDidLayoutSubviews() {
println(self.mapViewOutlet.frame.size)
}

Change Height of UIProgressView in Swift

I've been trying to change the size and rotation of a progress view to adopt the size of the screen. It would basically function have a filling glass effect on the screen of the phone.
I rotated it fairly harmlessly by using
self.masteryProgress.transform = CGAffineTransformMakeRotation((CGFloat(-90) / CGFloat(180.0) * CGFloat(M_PI)))
I got the size of the view encasing the screen using view.bounds trying to get the rect and the width x height. So I'm not stuck there.
I tried using .frame .drawRect but when I use those it either breaks them or does nothing.
The height attribute seems to be locked at a constant value of 2 in interfaceBuilder.
Any solutions before I build a custom animation?
I tried setting the height of the progress bar in interfaceBuilder like some other posts said, but I need to have the progress view fill the whole screen no matter what device it's running on so that solution won't work in my case.
I ended up using
CGAffineTransformScale(masteryProgress.transform, view.bounds.height / 1334, (view.bounds.width / 2))
Since I'm rotating the progressView vertically, I set the progress view to a width of 1334 (the height of the iPhone 6 Plus) and a height of 2. By dividing the height of the screen by these values it should resize to any phone perfectly.
You can scale it by set its transform like so:
Swift 2
masteryProgress.transform = CGAffineTransformScale(masteryProgress.transform, 1, 20)
Swift 3、4
masteryProgress.transform = masteryProgress.transform.scaledBy(x: 1, y: 20)
I faced problem while making the UIProgressView as round rect one. While using only the transform
masteryProgress.transform = CGAffineTransformMakeScale(1, 4)
I achieved which was not my final requirement.
while my final expectation was something like the following
I finally achieved it by following two steps
1. Set Height Constraint from Interface Builder:
2. Create a custom class of UIProgressView and override func layoutSubviews() to add a custom mask layer:
override func layoutSubviews() {
super.layoutSubviews()
let maskLayerPath = UIBezierPath(roundedRect: bounds, cornerRadius: 4.0)
let maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
maskLayer.path = maskLayerPath.cgPath
layer.mask = maskLayer
}
And here is the final result
Transform still seems to be the only way to accomplish this. Try adding it as an extension to UIProgressView like so.
extension UIProgressView {
#IBInspectable var barHeight : CGFloat {
get {
return transform.d * 2.0
}
set {
// 2.0 Refers to the default height of 2
let heightScale = newValue / 2.0
let c = center
transform = CGAffineTransformMakeScale(1.0, heightScale)
center = c
}
}
}
If you added UIProgressView using Interface Builder, just create height constraint and change to value what you need. That's it.
This will work:
masteryProgress.transform = CGAffineTransformMakeScale(1, 4)
Some modification of 'Sauvik Dolui' answer:
In UIBezierPath you should set cornerRadius: like (self.frame.height / 2).
This is more universal solution than use (4.0).
override func layoutSubviews() {
super.layoutSubviews()
let maskLayerPath = UIBezierPath(roundedRect: bounds, cornerRadius: self.frame.height / 2)
let maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
maskLayer.path = maskLayerPath.cgPath
layer.mask = maskLayer
}
In Swift 3, it's changed to CGAffineTransform(scaleX: CGFloat, y: CGFloat). So code would be:
masteryProgress.transform = CGAffineTransform(scaleX: 1, y: 4)
In Swift version 3.0.2 use this:
masteryProgress.transform = masteryProgress.transform.scaledBy(x: 1, y: 5)
I'm experiencing same with Swift - UIProgressView fills diagonally
After scale progressbar with
CGAffineTransformMakeScale(1.0, 5.0)
bar animation becomes filling from left top corner to right bottom.
Putting the UIProgressView inside a UIStackView and setting the UIStackView's constraints might work as well. At least worked for me
I tried the accepted answer for Swift 5, had a label pinned to the bottom of the progressView and the label was oddly stretched. To change the height without any other issues I used it's heightAnchor to set it to the height I wanted.
1- create a programmatic UIProgressView:
lazy var progressView: UIProgressView = {
let progressView = UIProgressView(progressViewStyle: .default)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.trackTintColor = .white
progressView.progressTintColor = .green
progressView.progress = 0
return progressView
}()
2- set the heightAnchor constraint:
progressView.heightAnchor.constraint(equalToConstant: 12).isActive = true
progressView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
progressView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
progressView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
It works for me:
progress.transform = progress.transform.scaledBy(x: 1, y: self.bounds.size.height)
"self.bounds.size.height" is the height you want
I gave it height constraint and it worked for me. I gave it 10 Constant. But if you are creating a universal app you can also use multipliers.

How to center a subview of UIView

I have a UIView inside a UIViewm and I want the inner UIView to be always centered inside the outer one, without it having to resize the width and height.
I've set the struts and springs so that it's on top/left/right/bottom without setting the resize. But it still doesn't center. Any idea?
You can do this and it will always work:
child.center = [parent convertPoint:parent.center fromView:parent.superview];
And for Swift:
child.center = parent.convert(parent.center, from:parent.superview)
Objective-C
yourSubView.center = CGPointMake(yourView.frame.size.width / 2,
yourView.frame.size.height / 2);
Swift
yourSubView.center = CGPoint(x: yourView.frame.size.width / 2,
y: yourView.frame.size.height / 2)
Before we'll begin, let's just remind that origin point is the Upper Left corner CGPoint of a view.
An important thing to understand about views and parents.
Lets take a look at this simple code, a view controller that adds to it's view a black square:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createDummyView()
super.view.backgroundColor = UIColor.cyanColor();
}
func createDummyView(){
var subView = UIView(frame: CGRect(x: 15, y: 50, width: 50 , height: 50));
super.view.addSubview(subView);
view.backgroundColor = UIColor.blackColor()
}
}
This will create this view:
the black rectangle origin and center does fit the same coordinates as it's parent
Now let's try to add subView another SubSubView, and giving subSubview same origin as subView, but make subSubView a child view of subView
We'll add this code:
var subSubView = UIView();
subSubView.frame.origin = subView.frame.origin;
subSubView.frame.size = CGSizeMake(20, 20);
subSubView.backgroundColor = UIColor.purpleColor()
subView.addSubview(subSubView)
And this is the result:
Because of this line:
subSubView.frame.origin = subView.frame.origin;
You expect for the purple rectangle's origin to be same as it's parent (the black rectangle) but it goes under it, and why is that?
Because when you add a view to another view, the subView frame "world" is now it's parent BOUND RECTANGLE, if you have a view that it's origin on the main screen is at coords (15,15) for all it's sub views, the upper left corner will be (0,0)
This is why you need to always refer to a parent by it's bound rectangle, which is the "world" of it's subViews, lets fix this line to:
subSubView.frame.origin = subView.bounds.origin;
And see the magic, the subSubview is now located exactly in it's parent origin:
So, you like "ok I only wanted to center my view by my parents view, what's the big deal?"
well, it isn't big deal, you just need to "translate" the parent Center point which is taken from it's frame to parent's bounds center
by doing this:
subSubView.center = subView.convertPoint(subView.center, fromView: subSubView);
You're actually telling him "take parents view center, and convert it into subSubView world".
And you'll get this result:
I would use:
self.childView.center = CGPointMake(CGRectGetMidX(self.parentView.bounds),
CGRectGetMidY(self.parentView.bounds));
I like to use the CGRect options...
SWIFT 3:
self.childView.center = CGPoint(x: self.parentView.bounds.midX,
y: self.parentView.bounds.midY);
1. If you have autolayout enabled:
Hint: For centering a view on another view with autolayout you can use same code for any two views sharing at least one parent view.
First of all disable child views autoresizing
UIView *view1, *view2;
[childview setTranslatesAutoresizingMaskIntoConstraints:NO];
If you are UIView+Autolayout or Purelayout:
[view1 autoAlignAxis:ALAxisHorizontal toSameAxisOfView:view2];
[view1 autoAlignAxis:ALAxisVertical toSameAxisOfView:view2];
If you are using only UIKit level autolayout methods:
[view1 addConstraints:({
#[ [NSLayoutConstraint
constraintWithItem:view1
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:view2
attribute:NSLayoutAttributeCenterX
multiplier:1.f constant:0.f],
[NSLayoutConstraint
constraintWithItem:view1
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:view2
attribute:NSLayoutAttributeCenterY
multiplier:1.f constant:0.f] ];
})];
2. Without autolayout:
I prefer:
UIView *parentView, *childView;
[childView setFrame:({
CGRect frame = childView.frame;
frame.origin.x = (parentView.frame.size.width - frame.size.width) / 2.0;
frame.origin.y = (parentView.frame.size.height - frame.size.height) / 2.0;
CGRectIntegral(frame);
})];
The easiest way:
child.center = parent.center
You can use
yourView.center = CGPointMake(CGRectGetMidX(superview.bounds), CGRectGetMidY(superview.bounds))
And In Swift 3.0
yourView.center = CGPoint(x: superview.bounds.midX, y: superview.bounds.midY)
Set this autoresizing mask to your inner view.
With IOS9 you can use the layout anchor API.
The code would look like this:
childview.centerXAnchor.constraintEqualToAnchor(parentView.centerXAnchor).active = true
childview.centerYAnchor.constraintEqualToAnchor(parentView.centerYAnchor).active = true
The advantage of this over CGPointMake or CGRect is that with those methods you are setting the center of the view to a constant but with this technique you are setting a relationship between the two views that will hold forever, no matter how the parentview changes.
Just be sure before you do this to do:
self.view.addSubview(parentView)
self.view.addSubView(chidview)
and to set the translatesAutoresizingMaskIntoConstraints for each view to false.
This will prevent crashing and AutoLayout from interfering.
Using the same center in the view and subview is the simplest way of doing it. You can do something like this,
UIView *innerView = ....;
innerView.view.center = self.view.center;
[self.view addSubView:innerView];
Another solution with PureLayout using autoCenterInSuperview.
// ...
UIView *innerView = [UIView newAutoLayoutView];
innerView.backgroundColor = [UIColor greenColor];
[innerView autoSetDimensionsToSize:CGSizeMake(100, 30)];
[outerview addSubview:innerView];
[innerView autoCenterInSuperview];
This is it how it looks like:
In c# or Xamarin.ios, we can use like this
imageView.Center = new CGPoint(tempView.Frame.Size.Width / 2,
tempView.Frame.Size.Height / 2);
This worked for me
childView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).isActive = true
childView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).isActive = true
I would use:
child.center = CGPointMake(parent.bounds.height / 2, parent.bounds.width / 2)
This is simple, short, and sweet. If you use #Hejazi's answer above and parent.center is set to anything other than (0,0) your subview will not be centered!
func callAlertView() {
UIView.animate(withDuration: 0, animations: {
let H = self.view.frame.height * 0.4
let W = self.view.frame.width * 0.9
let X = self.view.bounds.midX - (W/2)
let Y = self.view.bounds.midY - (H/2)
self.alertView.frame = CGRect(x:X, y: Y, width: W, height: H)
self.alertView.layer.borderWidth = 1
self.alertView.layer.borderColor = UIColor.red.cgColor
self.alertView.layer.cornerRadius = 16
self.alertView.layer.masksToBounds = true
self.view.addSubview(self.alertView)
})
}// calculation works adjust H and W according to your requirement