set UIslider thumb image in swift - ios

I want to set thumb of like musi app here is ss
I want
I have
Here is code
durationSlider.setThumbImage(UIImage(named: "Slider_indicator.png"), forState: UIControlState.Normal)
durationSlider.setThumbImage(UIImage(named: "Slider_indicator.png"), forState: UIControlState.Highlighted)

If you want to clip the top part of the thumbImage, you should use:
public func thumbRectForBounds(bounds: CGRect, trackRect rect: CGRect, value: Float) -> CGRect
So add this to your code:
durationSlider.thumbRectForBounds(...)
And set the CGRect of the thumbRect to have the value of durationSlider.frame.origin.y as its own origin.y.
Here's another temporary solution, I'll get back to the thumbRectForBounds method shortly:
let mask = CAGradientLayer(layer: durationSlider.layer)
let lineTop = (durationSlider.bounds.height/2 - 0.5) / durationSlider.bounds.height
mask.frame = durationSlider.bounds
mask.colors = [UIColor.clearColor().CGColor, UIColor.blackColor().CGColor]
mask.locations = [lineTop, lineTop]
durationSlider.layer.mask = mask

here you want to show only below part of the thumb now. so you need to make above half image clear. Please check the image i have attached with the answer
[slider setThumbImage:[UIImage imageNamed:#"1"] forState:UIControlStateNormal];
Try this and download scale the image if you need

Related

Best performance solution for background image of 100 UIButtons

I have a UICollectionView with approximately 100 cells with a rounded button inside each cell. 5 cells per row, so I have to scroll down and up to select the buttons.
When the buttons are selected, the background image changes. I've done this in several ways which I expose below. Maybe it is not a very demanding view, but I was wondering what is the less expensive approach in terms of performance.
One solution I found is using an extension of UIImage and setting the button.layer.cornerRadius like this:
extension UIImage {
class func imageWithColor(color: UIColor?) -> UIImage! {
let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
let context = UIGraphicsGetCurrentContext();
if let color = color {
color.setFill()
}
else {
UIColor.whiteColor().setFill()
}
CGContextFillRect(context, rect);
let image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image
}
and then, setting the button image background with:
button.layer.cornerRadius = (cell.bounds.width - 8) / 2
button.clipsToBounds = true
button.setBackgroundImage(UIImage.imageWithColor(UIColor.greenColor()), forState: UIControlState.Selected)
I've heard that setting the layer.cornerRadius is pretty expensive.
Another approach would be just designing an squared image, in Photoshop or similar, with a circle in the middle and letting the rest transparent and setting it as button background.
Or another option, that I still haven't tried, I think it could be making an 1 x 1 pixels image with a color and setting the filling of the background as tile(I still haven checked the code for this one). But I think this is pretty similar to the first way.
Would you solve this questions measuring the performance with any software or just knowing more deeply the Swift language?
Try applying mask to image. It's better because you do it once for each image. This should not affect scroll performance. Only what you need is mask image. It should be square image with white background and black circle in middle. Here you can find example (obj-c). Swift:
extension UIImage {
func maskedImage(mask: UIImage) -> UIImage {
let maskImgRef = mask.CGImage
let maskRef = CGImageMaskCreate(CGImageGetWidth(maskImgRef), CGImageGetHeight(maskImgRef), CGImageGetBitsPerComponent(maskImgRef), CGImageGetBitsPerPixel(maskImgRef), CGImageGetBytesPerRow(maskImgRef), CGImageGetDataProvider(maskImgRef), nil, false)
if let maskedRef = CGImageCreateWithMask(self.CGImage, maskRef) {
let maskedIm = UIImage(CGImage: maskedRef)
UIGraphicsBeginImageContext(maskedIm.size)
maskedIm.drawInRect(CGRect(origin: CGPointZero, size: maskedIm.size))
let img = UIGraphicsGetImageFromCurrentImageContext()
return img
}
return self
}
}
UPD: code above is helpful if your images isn't monochrome. If they are monochrome you can use UIButtons with system type instead of ImageViews. Just disable userInteraction, set circled image for this buttons and manipulate tintColor.

Adding a layer of color over a UIImage

I was hoping to make my UIImage "highlight" briefly upon being tapped. Not sure of the color yet, but let's say blue for arguments sake. So you tap the image, it briefly looks blue and then it navigates you to a details page to edit something on another screen.
From some initial reading it seems the right course of action is to use the Quartz framework and do this:
imageView.layer.backgroundColor = UIColor.blueColor()
imageView.layer.opacity = 0.7
I guess the idea would be you change the background of the layer behind the image, and then by setting the opacity of the image, the blue "bleeds through" a little bit, giving you a slightly blue image?
When I try the above, however, a blue border goes around the image itself, and based upon the opacity, the blue is either dark or light. The actual image does not become any more blue, but it does react to the opacity (meaning if I set it to something like .1, the image is very faded and barely visible). Why does the image react correctly, but not show blue?
Thanks so much!
As far as I know changing the opacity will change the opacity for the WHOLE view, meaning not just the UIImage that the UIImageView holds. So instead of fading to reveal the UIImageView's background color, instead the opacity of the whole view is just decreased as you're seeing.
Another way you could do it though would be to add an initially transparent UIView on top of your UIImageView and change its opacity instead:
UIView *blueCover = [[UIView alloc] initWithFrame: myImageView.frame];
blueCover.backgroundColor = [UIColor blueColor];
blueCover.layer.opacity = 0.0f;
[self.view addSubview: blueCover];
[UIView animateWithDuration:0.2f animations^{
blueCover.layer.opacity = 0.5f
}];
Here's how I use tint and tint opacities in IOS 9 with Swift -
//apply a color to an image
//ref - http://stackoverflow.com/questions/28427935/how-can-i-change-image-tintcolor
//ref - https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming
func getTintedImage() -> UIImageView {
var image : UIImage
var imageView : UIImageView
image = UIImage(named: "someAsset")!
let size : CGSize = image.size
let frame : CGRect = CGRectMake((UIScreen.mainScreen().bounds.width-86)/2, 600, size.width, size.height)
let redCover : UIView = UIView(frame: frame)
redCover.backgroundColor = UIColor.redColor()
redCover.layer.opacity = 0.75
imageView = UIImageView()
imageView.image = image.imageWithRenderingMode(UIImageRenderingMode.Automatic)
imageView.addSubview(redCover)
return imageView
}
Is this tap highlight perhaps something you could do with a UIButton? UIButton has all these states off the bat and might be a bit easier to work with, specially if there's something that actually needs to happen after you tap it. Worst case scenario is you have a UIButton that does not trigger any method when tapped.
You can try changing the tint color instead:
UIImage *image = [UIImage imageNamed:#"yourImageAsset"];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.tintColor = [UIColor blueColor];
imageView.image = image;

add UIImage in CALayer

I must add a UIImageView as subview of MapView. To do this I created a layer above the MapView. In this layer I want to put my image, but I get a white rectangle and nothing else. My image is not visible.
This is the code:
- (void)viewDidLoad
{
//......
CALayer *layer = [CALayer layer];
layer.backgroundColor = [[UIColor whiteColor] CGColor];
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
layer.bounds = CGRectMake(self.mapView.bounds.origin.x,
self.mapView.bounds.origin.y, 80, 300);
}
else
{
layer.bounds = CGRectMake(self.mapView.frame.origin.x,
self.mapView.frame.origin.y, 150, 700);
}
layer.contents = (id)[UIImage imageNamed:#"myImage.png"];
//the name is correct but in the output the image is not visible
[[self.mapView layer] addSublayer:layer];
[layer setNeedsDisplay];
}
This is a general answer for the sake of future viewers. It is based on the question title rather than the details of the original question.
How to add a UIImage to a CALayer
You can add an image to a view's layer simply by using its contents property:
myView.layer.contents = UIImage(named: "star")?.cgImage
Note that the UIImage needs to be converted to a CGImage.
If you wish to add the image in its own layer, you can do it like this:
let myLayer = CALayer()
let myImage = UIImage(named: "star")?.cgImage
myLayer.frame = myView.bounds
myLayer.contents = myImage
myView.layer.addSublayer(myLayer)
Modifying the appearance
The above code produces a view like this. The light blue is the UIView and the dark blue star is the UIImage.
As you can see, though, it looks pixelated. This is because the UIImage is smaller than the UIView so it is being scaled to fill the view, which is the default it you don't specify anything else.
The examples below show variations on the layer's contentsGravity property. The code looks like this:
myView.layer.contents = UIImage(named: "star")?.cgImage
myView.layer.contentsGravity = kCAGravityTop
myView.layer.isGeometryFlipped = true
In iOS, you may want to set the isGeometryFlipped property to true if you are doing anything with top or bottom gravity, otherwise it will be the opposite of what you expect. (Only the gravity is flipped vertically, not the content rendering. If you are having trouble with the content being flipped, see this answer.)
There are two UIView examples below for every contentsGravity setting, one view is larger than the UIImage and the other is smaller. This way you can see the effects of the scaling and gravity.
kCAGravityResize
This is the default.
kCAGravityResizeAspect
kCAGravityResizeAspectFill
kCAGravityCenter
kCAGravityTop
kCAGravityBottom
kCAGravityLeft
kCAGravityRight
kCAGravityTopLeft
kCAGravityTopRight
kCAGravityBottomLeft
kCAGravityBottomRight
Related
Content mode property of a view
Drawing a UIImage in drawRect with CGContextDrawImage
CALayer Tutorial: Getting Started
it has to be
layer.contents = (id)[UIImage imageNamed:#"myImage.png"].CGImage;
You can only put a CGImage into a layer, not an UIImage directly.
I removed
[layer setNeedsDisplay];
I do not know why, but it works!
You need to set the frame of the CALayer properly, because the default is CGRectZero.
2020, full simple examples
In this example, the image is simply the size of the overall view:
class ImageyView: UIView {
private lazy var im: CALayer = {
let l = CALayer()
l.contents = UIImage(named: "some_background")?.cgImage
l.contentsGravity = .resizeAspect
layer.addSublayer(l)
return l
}()
override func layoutSubviews() {
super.layoutSubviews()
im.frame = bounds
}
}
In this example it's a Button, with small icon you're adding at the top left:
class BroadcastButton: UIButton {
private lazy var im: CALayer = {
let l = CALayer()
l.contents = UIImage(named: "your_icon")?.cgImage
l.contentsGravity = .resizeAspect
layer.addSublayer(l)
return l
}()
override func layoutSubviews() {
super.layoutSubviews()
im.frame = CGRect(origin: CGPoint(x: 2, y: 2),
size: CGSize(width: 14, height: 14))
}
}
myView.layer.contents = UIImage(named: "star")?.cgImage
This should be a comment, but I lost few hours, so I'm making it a stand alone answer.
I was doing:
myView.layer.contents = UIImage(named: "star")
can you find what the problem is? Because contents is of type Any then this just compiles fine. But the correct code is:
myView.layer.contents = UIImage(named: "star").cgImage

How do I prevent a button's background image from stretching?

I'm allocating a UIButtonTypeCustom to a UIView with a background image that is smaller than the button's frame.
Reason why the image is smaller is because I'm trying to add more of a "target area" for the UIButton. However, the image is being scaled to the full size of the frame, rather than just being the image's size.
I have tried setting the UIButton and UIButton's imageView's contentMode property to UIViewContentModeScaleAspectFit, but no luck, the image still gets stretched out.
Is there a way to do what I'm trying to do programmatically?
Thanks in advance!
A lot of people make the same mistake you do in regards to button images and then jump through hoops trying to make the button behave as they expect it to. Let's clear this up once and for all:
A UIButton has two types of images it can display -- a foreground image and a background image. The background image for a button is expected to replace the button's background texture. As such, it makes sense that it stretches to fill the entire background. However, the button's foreground image is expected to be an icon that may or may not display alongside text; it will not stretch. It may shrink if the frame is smaller than the image, but it will not stretch. You can even set the alignment of the foreground image using the Control alignment properties in Interface Builder.
A button's foreground and background image can be set in code like this:
// stretchy
[self setBackgroundImage:backgroundImage forState:UIControlStateNormal];
// not stretchy
[self setImage:forgroundImage forState:UIControlStateNormal];
You don't have access to the background imageView, but there is fully working workaround:
EDIT: There is an even better workaround then what I posted originally.
You can create a UIImage from any color, and call -setBackgroundImage:forState.
See bradley's answer, here:
https://stackoverflow.com/a/20303841/1147286
Original answer:
Instead of calling -setBackgroundImage:forState:, create a new UIImageView and add it as a subview of the button.
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:img];
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
[bgImageView setFrame:CGRectMake(0, 0, videoButton.frame.size.width, videoButton.frame.size.height)];
bgImageView.tag = 99;
[yourButton addSubview:bgImageView];
[yourButton bringSubviewToFront:yourButton.imageView];
Create the imageview
Set the content mode and frame
I also set a recognizable tag, so that when the screen rotates I can easily find my custom imageView in the button's subviews and reset its frame
Add it as a subview to the button
Bring the frontal imageView of the button to the front so our custom imageView doesn't overlap it
When the button needs to rotate just find the imageView by its tag and reset its frame:
UIImageView *bgImageView = (UIImageView *)[button viewWithTag:99];
[bgImageView setFrame:CGRectMake(0, 0, newWidth, newHeight)];
The cleanest and easiest way it probably to use the title insets of the button.
You set your image as the button image, and then you change the left title inset to match minus the width of your image:
myButton.titleEdgeInsets = UIEdgeInsetsMake(0, -myImage.width, 0, 0)
This will move the text back where it was before the image was added to its left. You can also use this value to add some padding to you button.
Stumbled on this problem too.
Adding image programmatically, as memmons thoroughly explained, did not help:(
I had a button 100x40 and image 100x100, it would appear squeezed, not fitted, as one would infer from "Aspect Fit" option. Actually, non of those view options had an effect.
I just had to rescale it so it would fit on a button, then use setImage:
UIImage *img=[UIImage imageNamed:#"myimage.png"];
CGImageRef imgRef = [img CGImage];
CGFloat imgW = CGImageGetWidth(imgRef);
CGFloat imgH = CGImageGetHeight(imgRef);
CGFloat btnW = myBttn.frame.size.width;
CGFloat btnH = myBttn.frame.size.height;
//get lesser button dimension
CGFloat minBtn=btnW;
if (btnW>btnH) {
minBtn=btnH;
}
//calculate scale using greater image dimension
CGFloat scl=imgH/minBtn;
if (imgW>imgH) {
scl=imgW/minBtn;
}
//scale image
UIImage *scaledImage = [UIImage imageWithCGImage:[img CGImage] scale:(img.scale * scl) orientation:(img.imageOrientation)];
//clean up
UIGraphicsEndImageContext();
//set it on a button
[myBttn setImage:scaledImage forState:UIControlStateNormal];
It is simple as:
ImageBtn.imageView?.contentMode = .scaleAspectFit
ImageBtn.setImage(chosenImage, for: .normal)
Another consideration is the BaseLine constraint. If your buttons have this constraint set (depicted as a horizontal or vertical line through multiple controls on your layout), it will cause your images to stretch without stretching the underlying button control. If your button is otherwise properly constrained (leading/trailing and top/bottom spaces, and etc...) removing the BaseLine constraint should have no impact on the layout, while allowing the foreground image to scale properly to the underlying button shape.
Answerbot answers the question with what is proper and correct to do. Don't fight the OS and use things as intended is always good advice. However, sometimes you need to break the rules.
I was able to mask the enlarged background image (not prevent it) by overlaying it with a black CAlayer then overlaying again with a properly resized image CAlayer. This was all done by creating a subclass of UIButton and overwriting the setHighlighted method.
NEED CODE?
- (void)setHighlighted:(BOOL)highlighted
{
super.highlighted = highlighted;
//
//Whenever an image needs to be highlighted, create a dimmed new image that is correctly sized. Below it is a englarged stretched image.
//
if (highlighted != _previousHighlightedSate)
{
_previousHighlightedSate = highlighted;
if (highlighted)
{
//Create a black layer so image can dim
_blackLayer = [CALayer layer];
_blackLayer.bounds = self.bounds;
CGRect rect = _blackLayer.bounds;
rect.size.width = rect.size.width*2;
rect.size.height = rect.size.height*2;
_blackLayer.bounds = rect;
_blackLayer.backgroundColor = [[UIColor blackColor] CGColor];
//create image layer
_nonStretchImageLayer = [CALayer layer];
_nonStretchImageLayer.backgroundColor = [UIColor blackColor].CGColor;
_nonStretchImageLayer.bounds = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.frame = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.contentsGravity = kCAGravityResizeAspect;//default is to resize
_nonStretchImageLayer.contents = (id)self.imageView.image.CGImage;
_nonStretchImageLayer.opacity = 0.5;
//add layers to image view
[self.imageView.layer addSublayer:_blackLayer];
[self.imageView.layer addSublayer:_nonStretchImageLayer];
}
else
{
//remove from image view
[_blackLayer removeFromSuperlayer];
[_nonStretchImageLayer removeFromSuperlayer];
//nil them out.
_blackLayer = nil;
_nonStretchImageLayer = nil;
}
}
Inspiration for this work around came from here
I guess the easiest solution is to use UIImage's resizableImageWithCapInsets method. Use UIEdgeInsetsMake to configure the free spaces.
might help someone
button.subviews.first?.contentMode = .scaleAspectFit
Swift version of Zoltán Matók answer
Just a copy of my code using SnapKit to do auto layout and syntatic sugar Then library for initilizations, it should work similar for normal Apples way of programatic layout.
let backButton = UIButton(type: .custom).then { (button) in
let image = UIImage(named: "backButtonIcon")?.withRenderingMode(.alwaysOriginal)
let imageView = UIImageView(image: image)
button.addSubview(imageView)
imageView.snp.makeConstraints { (make) in
make.center.equalTo(button.snp.center)
}
button.bringSubviewToFront(imageView)
button.contentMode = .scaleAspectFit
button.backgroundColor = .clear
button.isUserInteractionEnabled = true
button.isEnabled = true
button.imageView?.contentMode = .scaleAspectFit
button.imageView?.snp.makeConstraints({ (make) in
make.height.width.equalTo(24)
})
button.snp.makeConstraints { (make) in
make.height.width.equalTo(24)
}
}
You can use uibutton.imageView.contentMode for no stretching:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(posX, posY, widthButton, heightButton);
[button setTitle:#"" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamed"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamedHighlighted"] forState:UIControlStateHighlighted];
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
[button addTarget:self action:#selector(functionMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
What you need to do is add your image as a UIImageView.
Than add a button with transperent background (UIColor ClearColor) on top of it with your desired width and height.

How to round the corners of a button

I have a rectangle image (jpg) and want to use it to fill the background of a button with rounded corner in xcode.
I wrote the following:
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
CGRect frame = CGRectMake(x, y, cardWidth, cardHeight);
button.frame = frame;
[button setBackgroundImage:backImage forState:UIControlStateNormal];
However, the button I get with that approach doesn't have its corners rounded: it is instead a plain rectangle that looks exactly like my original image. How can I get instead an image with rounded corner to represent my button?
Thanks!
I tried the following solution with the UITextArea and I expect this will work with UIButton as well.
First of all import this in your .m file -
#import <QuartzCore/QuartzCore.h>
and then in your loadView method add following lines
yourButton.layer.cornerRadius = 10; // this value vary as per your desire
yourButton.clipsToBounds = YES;
You can achieve by this RunTime Attributes
we can make custom button.just see screenshot attached.
kindly pay attention :
in runtime attributes to change color of border follow this instruction
create category class of CALayer
in h file
#property(nonatomic, assign) UIColor* borderIBColor;
in m file:
-(void)setBorderIBColor:(UIColor*)color {
self.borderColor = color.CGColor;
}
-(UIColor*)borderIBColor {
return [UIColor colorWithCGColor:self.borderColor];
}
now onwards to set border color check screenshot
thanks
Pushing to the limits corner radius up to get a circle:
self.btnFoldButton.layer.cornerRadius = self.btnFoldButton.frame.height/2.0;
If button frame is an square it does not matter frame.height or frame.width. Otherwise use the largest of both ones.
You may want to check out my library called DCKit. It's written on the latest version of Swift.
You'd be able to make a rounded corner button/text field from the Interface builder directly:
It also has many other cool features, such as text fields with validation, controls with borders, dashed borders, circle and hairline views etc.
UIButton* closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(10, 50, 90, 35)];
//Customise this button as you wish then
closeBtn.layer.cornerRadius = 10;
closeBtn.layer.masksToBounds = YES;//Important
Import QuartCore framework if it is not there in your existing project, then import #import <QuartzCore/QuartzCore.h> in viewcontroller.m
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect]];
CGRect frame = CGRectMake(x, y, width, height); // set values as per your requirement
button.layer.cornerRadius = 10;
button.clipsToBounds = YES;
First set width=100 and Height=100 of button
Objective C Solution
YourBtn1.layer.cornerRadius=YourBtn1.Frame.size.width/2;
YourBtn1.layer.borderColor=[uicolor blackColor].CGColor;
YourBtn1.layer.borderWidth=1.0f;
Swift 4 Solution
YourBtn1.layer.cornerRadius = YourBtn1.Frame.size.width/2
YourBtn1.layer.borderColor = UIColor.black.cgColor
YourBtn1.layer.borderWidth = 1.0
Try my code. Here you can set all properties of UIButton like text colour, background colour, corner radius, etc.
extension UIButton {
func btnCorner() {
layer.cornerRadius = 10
clipsToBounds = true
backgroundColor = .blue
}
}
Now call like this
yourBtnName.btnCorner()
For Objective C:
submitButton.layer.cornerRadius = 5;
submitButton.clipsToBounds = YES;
For Swift:
submitButton.layer.cornerRadius = 5
submitButton.clipsToBounds = true
If you want a rounded corner only to one corner or two corners, etc... read this post:
[ObjC] – UIButton with rounded corner - http://goo.gl/kfzvKP
It's a XIB/Storyboard subclass. Import and set borders without write code.
For Swift:
button.layer.cornerRadius = 10.0
updated for Swift 3 :
used below code to make UIButton corner round:
yourButtonOutletName.layer.cornerRadius = 0.3 *
yourButtonOutletName.frame.size.height
Swift 4 Update
I also tried many options still i wasn't able to get my UIButton round cornered.
I added the corner radius code inside the viewDidLayoutSubviews() Solved My issue.
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
anyButton.layer.cornerRadius = anyButton.frame.height / 2
}
Also we can adjust the cornerRadius as follows:
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
anyButton.layer.cornerRadius = 10 //Any suitable number as you prefer can be applied
}
An alternative answer which sets a border too (making it more like a button) is here ... How to set rectangle border for custom type UIButton
For iOS SWift 4
button.layer.cornerRadius = 25;
button.layer.masksToBounds = true;
You can use outlet connection and didSet function for your button on the view;
#IBOutlet weak var button: UIButton!{
didSet {
button.layer.cornerRadius = 5;
button.layer.masksToBounds = true;
}
}

Resources