Swift Backgroung image not stretching to bounds - ios

I'm currently trying to add a background image to my navigation bar but the background image itself is not stretching to fill the bounds of the specified space (the pink button should be covering the blue square or atleast getting close to same size).
How do I get the background image to stretch/ fill the space?
Screenshot:
How I add the button:
let newsButton = UIButton(type: .custom)
newsButton.translatesAutoresizingMaskIntoConstraints = false
newsButton.backgroundColor = .blue
newsButton.setTitle(NSLocalizedString("News", comment: "News button"), for: .normal)
newsButton.layer.cornerRadius = 7
newsButton.titleLabel?.font = .systemFont(ofSize: 20)
newsButton.addTarget(self, action: #selector(onClick(_:)), for: .touchUpInside)
if let image = UIImage(named: "pink_button") {
newsButton.setBackgroundImage(image, for: .normal)
}
NSLayoutConstraint.activate([
newsButton.widthAnchor.constraint(equalToConstant: 128),
newsButton.heightAnchor.constraint(equalToConstant: 43)
])
navigationItem.titleView = newsButton

Your image has a lot of transparent space around the "red" shape.
If you replace it with this image (I trimmed out the alpha areas):
It will look like this:
As an alternative to using a "stretched image" you could use this custom view (that draws your shape) and embed the button as a subview:
class MyBottomShadowView: UIView {
var radius: CGFloat = 8
var primaryColor: UIColor = .red
var shadowColor: UIColor = .gray
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// background color needs to be .clear
self.backgroundColor = .clear
}
override func draw(_ rect: CGRect) {
super.draw(rect)
var r: CGRect!
var pth: UIBezierPath!
// if rounded rect for "bottom shadow line"
// goes all the way to the top, we'll get
// anti-alias artifacts at the top corners
// so, we'll make it slightly smaller and
// move it down from the top
r = bounds.insetBy(dx: 0, dy: 2).offsetBy(dx: 0, dy: 2)
pth = UIBezierPath(roundedRect: r, cornerRadius: radius)
shadowColor.setFill()
pth.fill()
// "filled" rounded rect should be
// 2-points shorter than height
r = bounds
r.size.height -= 2.0
pth = UIBezierPath(roundedRect: r, cornerRadius: radius)
primaryColor.setFill()
pth.fill()
}
}
Your setup then becomes:
let newsButton = UIButton(type: .custom)
newsButton.translatesAutoresizingMaskIntoConstraints = false
newsButton.setTitleColor(.white, for: .normal)
newsButton.setTitleColor(.lightGray, for: .highlighted)
newsButton.setTitle(NSLocalizedString("News", comment: "News button"), for: .normal)
newsButton.titleLabel?.font = .systemFont(ofSize: 20)
// set button background to clear
newsButton.backgroundColor = .clear
newsButton.addTarget(self, action: #selector(onClick(_:)), for: .touchUpInside)
// create "bottom shadow view"
let shadView = MyBottomShadowView()
// set radius, primary and shadow colors as desired
shadView.radius = 12
shadView.primaryColor = UIColor(red: 1.00, green: 0.25, blue: 0.40, alpha: 1.0)
shadView.shadowColor = UIColor(red: 0.65, green: 0.20, blue: 0.30, alpha: 1.0)
shadView.translatesAutoresizingMaskIntoConstraints = false
shadView.addSubview(newsButton)
NSLayoutConstraint.activate([
shadView.widthAnchor.constraint(equalToConstant: 128),
shadView.heightAnchor.constraint(equalToConstant: 43),
newsButton.topAnchor.constraint(equalTo: shadView.topAnchor),
newsButton.leadingAnchor.constraint(equalTo: shadView.leadingAnchor),
newsButton.trailingAnchor.constraint(equalTo: shadView.trailingAnchor),
newsButton.bottomAnchor.constraint(equalTo: shadView.bottomAnchor),
])
navigationItem.titleView = shadView
And it looks like this:

Related

Fix two colours on UISlider swift

Slider with two different colors
How can we make a slider with two fixed colors? The colors won't change even if the slider is moving. Also, the slider thumb should be able to side over any of those two colors. I should be able to define the length of the first section.
func createSlider(slider:UISlider) {
let frame = CGRect(x: 0.0, y: 0.0, width: slider.bounds.width, height: 4)
let tgl = CAGradientLayer()
tgl.frame = frame
tgl.colors = [UIColor.gray.cgColor,UIColor.red.cgColor]
tgl.endPoint = CGPoint(x: 0.4, y: 1.0)
tgl.startPoint = CGPoint(x: 0.0, y: 1.0)
UIGraphicsBeginImageContextWithOptions(tgl.frame.size, false, 0.0)
tgl.render(in: UIGraphicsGetCurrentContext()!)
let backgroundImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
slider.setMaximumTrackImage(backgroundImage?.resizableImage(withCapInsets:.zero), for: .normal)
slider.setMinimumTrackImage(backgroundImage?.resizableImage(withCapInsets:.zero), for: .normal)
}
I have already tried this. But this dosent give me exactly what I wanted to achieve.
Here's one approach...
set both min and max track images to "empty" images
add left and right subviews to act as "fake" track images
add a different color sublayer on each side subview
when you slide the thumb, update the frames of the layers
Here's some example code... you may want to do some tweaks:
class XebSlider: UISlider {
private var colors: [[UIColor]] = [
[.black, .black],
[.black, .black],
]
// left and right views will become the "fake" track images
private let leftView = UIView()
private let rightView = UIView()
// each view needs a layer to "change color"
private let leftShape = CALayer()
private let rightShape = CALayer()
// constraints that will be updated
private var lvWidth: NSLayoutConstraint!
private var lvLeading: NSLayoutConstraint!
private var rvTrailing: NSLayoutConstraint!
// how wide the two "sides" should be
private var leftPercent: CGFloat = 0
private var rightPercent: CGFloat = 0
// track our current width, so we don't repeat constraint updates
// unless our width has changed
private var currentWidth: CGFloat = 0
init(withLeftSidePercent leftWidth: CGFloat, leftColors: [UIColor], rightColors: [UIColor]) {
super.init(frame: .zero)
commonInit()
leftView.backgroundColor = leftColors[1]
rightView.backgroundColor = rightColors[1]
leftShape.backgroundColor = leftColors[0].cgColor
rightShape.backgroundColor = rightColors[0].cgColor
leftPercent = leftWidth
rightPercent = 1.0 - leftPercent
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// set both track images to "empty" images
setMinimumTrackImage(UIImage(), for: [])
setMaximumTrackImage(UIImage(), for: [])
// add left and right subviews
[leftView, rightView].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
v.layer.cornerRadius = 4.0
v.layer.masksToBounds = true
v.isUserInteractionEnabled = false
insertSubview(v, at: 0)
}
// add sublayers
leftView.layer.addSublayer(leftShape)
rightView.layer.addSublayer(rightShape)
// create constraints whose .constant values will be modified in layoutSubviews()
lvLeading = leftView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0)
rvTrailing = rightView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0)
lvWidth = leftView.widthAnchor.constraint(equalToConstant: 0.0)
// avoids auto-layout complaints when the frame changes (such as on device rotation)
lvWidth.priority = UILayoutPriority(rawValue: 999)
// set constraints for subviews
NSLayoutConstraint.activate([
lvLeading,
rvTrailing,
lvWidth,
leftView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0.0),
leftView.heightAnchor.constraint(equalToConstant: 8.0),
rightView.centerYAnchor.constraint(equalTo: leftView.centerYAnchor),
rightView.heightAnchor.constraint(equalTo: leftView.heightAnchor),
rightView.leadingAnchor.constraint(equalTo: leftView.trailingAnchor, constant: 1.0),
])
}
override func layoutSubviews() {
super.layoutSubviews()
// we only want to do this if the bounds width has changed
if bounds.width != currentWidth {
let trackRect = self.trackRect(forBounds: bounds)
lvLeading.constant = trackRect.origin.x
rvTrailing.constant = -(bounds.width - (trackRect.origin.x + trackRect.width))
lvWidth.constant = trackRect.width * leftPercent
}
// get percentage of thumb position
// based on min and max values
let pctValue = (self.value - self.minimumValue) / (self.maximumValue - self.minimumValue)
// calculate percentage of each side that needs to be "covered"
// by the different color layer
let leftVal = max(0.0, min(CGFloat(pctValue) / leftPercent, 1.0))
let rightVal = max(0.0, min((CGFloat(pctValue) - leftPercent) / rightPercent, 1.0))
var rLeft = leftView.bounds
var rRight = rightView.bounds
rLeft.size.width = leftView.bounds.width * leftVal
rRight.size.width = rightView.bounds.width * rightVal
// disable default layer animations
CATransaction.begin()
CATransaction.setDisableActions(true)
leftShape.frame = rLeft
rightShape.frame = rRight
CATransaction.commit()
}
}
and a controller example showing its usage:
class ViewController: UIViewController {
var slider: XebSlider!
override func viewDidLoad() {
super.viewDidLoad()
let leftSideColors: [UIColor] = [
#colorLiteral(red: 0.4796532989, green: 0.4797258377, blue: 0.4796373844, alpha: 1),
#colorLiteral(red: 0.8382737041, green: 0.8332912326, blue: 0.8421040773, alpha: 1),
]
let rightSideColors: [UIColor] = [
#colorLiteral(red: 0.9009097219, green: 0.3499996662, blue: 0.4638580084, alpha: 1),
#colorLiteral(red: 0.9591985345, green: 0.8522816896, blue: 0.8730568886, alpha: 1),
]
let leftSideWidthPercent: CGFloat = 0.5
slider = XebSlider(withLeftSidePercent: leftSideWidthPercent, leftColors: leftSideColors, rightColors: rightSideColors)
view.addSubview(slider)
slider.translatesAutoresizingMaskIntoConstraints = false
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain slider 40-pts from Top / Leading / Trailing
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
slider.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
])
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
// to get teh custom slider to update properly
self.slider.setNeedsLayout()
}, completion: {
_ in
})
}
}
Result:

UISearchBar with a white background is impossible?

I really thought it would be easy to set the background color of my UISearchBar's text field to white. But no matter what I try, it always stays offwhite / light gray (#efeff0).
import UIKit
class ViewController: UIViewController {
private let searchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Hello World"
view.backgroundColor = #colorLiteral(red: 0.9588784575, green: 0.9528519511, blue: 0.9350754619, alpha: 1)
searchController.searchBar.searchTextField.backgroundColor = .white
navigationItem.searchController = searchController
}
}
How can I make the search bar have a pure white background color? App is iOS 13+, if that helps.
Tiny test project: https://github.com/kevinrenskers/WhiteSearch.
It's possible. Set the background of the search field with a white image.
let size = CGSize(width: searchController.searchBar.frame.size.width - 12, height: searchController.searchBar.frame.size.height - 12)
let backgroundImage = createWhiteBG(size)!
let imageWithCorner = backgroundImage.createImageWithRoundBorder(cornerRadiuos: 10)!
searchController.searchBar.setSearchFieldBackgroundImage(imageWithCorner, for: UIControl.State.normal)
If you don't want to input an image to app. Try this for create one programmatically.
func createWhiteBG(_ frame : CGSize) -> UIImage? {
var rect = CGRect(x: 0, y: 0, width: 0, height: 0)
rect.size = frame
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(UIColor.white.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
extension UIImage {
func createImageWithRoundBorder(cornerRadiuos : CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, scale)
let rect = CGRect(origin:CGPoint(x: 0, y: 0), size: self.size)
let context = UIGraphicsGetCurrentContext()
let path = UIBezierPath(
roundedRect: rect,
cornerRadius: cornerRadiuos
)
context?.beginPath()
context?.addPath(path.cgPath)
context?.closePath()
context?.clip()
self.draw(at: CGPoint.zero)
context?.restoreGState()
path.lineWidth = 1.5
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Try this ... Change colors and images according to your preference
DispatchQueue.main.async {
searchBar.backgroundImage = UIImage()
for s in searchBar.subviews[0].subviews {
if s is UITextField {
s.layer.borderWidth = 1.0
s.layer.borderColor = UIColor.lightGray.cgColor
}
}
let searchTextField:UITextField = searchBar.subviews[0].subviews.last as? UITextField ?? UITextField()
searchTextField.layer.cornerRadius = 10
searchTextField.textAlignment = NSTextAlignment.left
let image:UIImage = UIImage(named: "search")!
let imageView:UIImageView = UIImageView.init(image: image)
searchTextField.leftView = nil
searchTextField.placeholder = "Search..."
searchTextField.font = UIFont.textFieldText
searchTextField.rightView = imageView
searchTextField.rightViewMode = UITextField.ViewMode.always
}
Here is My complete Custom Search Bar Which you can define the searchbar backgroundColor and TextField background Color
Tested
import Foundation
class SearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
makeUI()
}
private func makeUI( ) {
//SearchBar BackgroundColor
self.backgroundImage = UIImage(color: UIColor.white)
//Border Width
self.layer.borderWidth = 1
//Border Color
self.layer.borderColor = UIColor("DEDEDE")?.cgColor
//Corner Radius
self.layer.cornerRadius = 3
self.layer.masksToBounds = true
//Change Icon
self.setImage(UIImage(named: "search")?
.byResize(to: CGSize(width: 30, height: 30)), for: .search, state: .normal)
if let searchTextField = self.value(forKey: "searchField") as? UISearchTextField {
//TextField Background !!!!!
searchTextField.backgroundColor = UIColor.white
//TextField Font
searchTextField.font = UIFont(name: "Poppins-Regular", size: 21)
searchTextField.textColor = .black
}
}
}

UIButton with cornerRadius, shadow and background for state

This seems to be a tricky problem I cannot find the solution for.
I need a UIButton with:
rounded corners,
background color for state,
drop shadow.
I can achieve the the corner radius and background color for state subclassing a UIButton and setting the parameters:
// The button is set to "type: Custom" in the interface builder.
// 1. rounded corners
self.layer.cornerRadius = 10.0
// 2. Color for state
self.backgroundColor = UIColor.orange // for state .normal
self.setBackgroundColor(color: UIColor.red, forState: .highlighted)
// 3. Add the shadow
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 3)
self.layer.shadowOpacity = 0.3
self.layer.shadowRadius = 6.0
self.layer.masksToBounds = false
// I'm also using an extension to be able to set the background color
extension UIButton {
func setBackgroundColor(color: UIColor, forState: UIControl.State) {
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
if let context = UIGraphicsGetCurrentContext() {
context.setFillColor(color.cgColor)
context.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
let colorImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.setBackgroundImage(colorImage, for: forState)
}
}
}
The problem:
Once the button is rendered, seems fine, has the desired background color, rounded corners and the shadow. But once is pressed the setBackgroundImage(image, for: forState) launches and gets rid of the radius, so the button is displayed as a square with the desired color and shadow.
Is there a way to preserve the radius when the button is pressed (.highlighted)?
I did try the solutions from this post (for example) but none consider the setBackgroundImage(image, for: forState). I cannot find anything that works...
If you just want to change the background color for .normal and .highlighted - that is, you don't need a background image - you can override var isHighlighted to handle the color changes.
Here is an example UIButton subclass. It is marked #IBDesignable and has normalBackground and highlightBackground colors marked as #IBInspectable so you can set them in Storyboard / Interface Builder:
#IBDesignable
class HighlightButton: UIButton {
#IBInspectable
var normalBackground: UIColor = .clear {
didSet {
backgroundColor = self.normalBackground
}
}
#IBInspectable
var highlightBackground: UIColor = .clear
override open var isHighlighted: Bool {
didSet {
backgroundColor = isHighlighted ? highlightBackground : normalBackground
}
}
func setBackgroundColor(_ c: UIColor, forState: UIControl.State) -> Void {
if forState == UIControl.State.normal {
normalBackground = c
} else if forState == UIControl.State.highlighted {
highlightBackground = c
} else {
// implement other states as desired
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
// 1. rounded corners
self.layer.cornerRadius = 10.0
// 2. Default Colors for state
self.setBackgroundColor(.orange, forState: .normal)
self.setBackgroundColor(.red, forState: .highlighted)
// 3. Add the shadow
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 3)
self.layer.shadowOpacity = 0.3
self.layer.shadowRadius = 6.0
self.layer.masksToBounds = false
}
}

Button Not Fully Drawn

I have a button with text and an image on it. It gets set up in viewDidAppear and then in the IBAction I change the Attributed title. For some reason the button background color doesn't completely cover the button on the initial draw. It leaves a horizontal sliver of white. I found that by running my formatButton function in the IBAction subsequent button presses show a properly drawn button. But I can't get the first loaded view of the button to look right. Any ideas?
I found that by formatting in the IBAction it fixed it for future button draws but a sendAction(.touchUpInside) couldn't even fake it into fixing the draw problem. (It did change the button text like the IBAction makes it though.)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
formatButton(btn: searchTitlesButton)
formatButton(btn: searchPeopleButton)
formatButton(btn: searchCategoryButton)
searchTitlesButton.setTitle("Title", for: .normal)
searchPeopleButton.setTitle("Actor", for: .normal)
//searchCategoryButton.setTitle(categoryList[searchCategoryIndex], for: .normal)
let fullString = NSMutableAttributedString()
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(named:"DownArrow")
let imageString = NSAttributedString(attachment: imageAttachment)
fullString.append(NSAttributedString(string: categoryList[searchCategoryIndex]+" "))
fullString.append(imageString)
searchCategoryButton.setAttributedTitle(fullString, for: .normal)
formatButton(btn: searchCategoryButton)
postTableView.rowHeight = CGFloat(120)
}
#IBAction func searchCategoryButton(_ sender: Any) {
if searchCategoryIndex < categoryList.count - 1 {
searchCategoryIndex += 1
} else {
searchCategoryIndex = 0
}
// Going to try and make a formatted label with a string and image of a down arrow.
let fullString = NSMutableAttributedString()
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(named:"DownArrow")
let imageString = NSAttributedString(attachment: imageAttachment)
fullString.append(NSAttributedString(string: categoryList[searchCategoryIndex]+" "))
fullString.append(imageString)
searchCategoryButton.setAttributedTitle(fullString, for: .normal)
formatButton(btn: searchCategoryButton)
}
func formatButton(btn:UIButton) {
btn.layer.cornerRadius = 5
btn.layer.borderWidth = 1
btn.layer.borderColor = UIColor.black.cgColor
btn.setTitleColor(UIColor.white, for: .normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.bold)
let gradientLayer = CAGradientLayer()
gradientLayer.frame = btn.bounds
let bottomColor = UIColor(red: CGFloat(25/255.0), green: CGFloat(113/255.0), blue: CGFloat(255/255.0), alpha: CGFloat(1.0))
gradientLayer.colors = [UIColor.white.cgColor, bottomColor.cgColor]
btn.layer.insertSublayer(gradientLayer, at: 0)
btn.clipsToBounds = true
}
The reason why the background gradient doesn't fully cover the button, it probably because the size of the button changes when you set the attributed title. The best way to solve this, is by creating a subclass of UIButton, so that you can update the frame of your custom gradient layer, whenever the button's bounds change. For example:
class GradientButton: UIButton {
private let gradientLayer = CAGradientLayer()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
layer.cornerRadius = 5
layer.borderWidth = 1
layer.borderColor = UIColor.black.cgColor
setTitleColor(UIColor.white, for: .normal)
titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.bold)
let bottomColor = UIColor(red: CGFloat(25/255.0), green: CGFloat(113/255.0), blue: CGFloat(255/255.0), alpha: CGFloat(1.0))
gradientLayer.colors = [UIColor.white.cgColor, bottomColor.cgColor]
layer.insertSublayer(gradientLayer, at: 0)
clipsToBounds = true
}
override var bounds: CGRect {
didSet {
gradientLayer.frame = layer.bounds
}
}
}
Then in the storyboard of nib you can change the class of the button to GradientButton. It should now automatically apply the gradient styling, and update the frame whenever the bounds of the button change.
I hope you find this useful. Let me know if you are still having issues.

Change Button Color on state change

I want for the buttons to have white background and blue title when highlighted. I have an extension of UIButton to set its background color.
extension UIButton {
func setBackgroundColor(color: UIColor, forState: UIControlState) {
UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
let colorImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.setBackgroundImage(colorImage, for: forState)
self.clipsToBounds = true
}
}
and in the next function, I set up a particular button.
private func stylizingButton(button: UIButton){
button.layer.borderWidth = 2
button.layer.borderColor = textColor.cgColor
button.layer.cornerRadius = 8
button.setTitleColor(textColor, for: .normal)
button.setTitleColor(backgroundColor, for: .highlighted)
button.setBackgroundColor(color: .white, forState: .highlighted)
}
When I change the background color of the button to black, the result is some dark blue color. It is like the screen background color and the button's color are mixing.
Create a custom class for your button and handle your color changing properties on state like below.
class MyButton: UIButton {
fileprivate var titleColorNormal: UIColor = .white
fileprivate var titleColorHighlighted: UIColor = .blue
fileprivate var backgroundColorNormal: UIColor = .blue
fileprivate var backgroundColorHighlighted: UIColor = .white
override var isHighlighted: Bool {
willSet(newValue){
if newValue {
self.setTitleColor(titleColorHighlighted, for: state)
self.backgroundColor = backgroundColorHighlighted
}else {
self.setTitleColor(titleColorNormal, for: state)
self.backgroundColor = backgroundColorNormal
}
}
}
}
Either make image for the whole size, or make it stretchable, so it can fill the whole background:
extension UIButton {
func setBackgroundColor(color: UIColor, for state: UIControlState) {
let rect = CGRect(origin: CGPoint(x: 0, y:0), size: CGSize(width: 1, height: 1))
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
context.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let insets = UIEdgeInsetsMake(0, 0, 0, 0)
let stretchable = image!.resizableImage(withCapInsets: insets, resizingMode: .tile)
self.setBackgroundImage(stretchable, for: state)
}
}
I had the same problem with the mixing colors in highlighted state but didn't want to create a custom class. I found out that you can simply change the button type from "System" to "Custom". Then you can use the functions for setting colors by state. The colors will be displayed as defined.
You can change the button type in interface builder.

Resources