How to get rid of border of UISearchBar? - swift

The Problem
I have a UISearchController embedded within a UIView. The searchbar takes up the entire size of the view. However, there's this grey border that I can't figure how to get rid off. I want the search bar to take up the entire size of the view without that pesky grey border. How can I get rid of it?
I've tried
searchController?.searchBar.backgroundImage = UIImage()
But all that does is make the grey border clear (not to mention it makes the searchResultsController of the searchController not show up):
I want the border to be gone and the search bar take up the entire frame of the UIView.

let SEARCH_BAR_SEARCH_FIELD_KEY = "searchField"
let SEARCH_BAR_PLACEHOLDER_TEXT_KEY = "_placeholderLabel.textColor"
func customiseSearchBar() {
searchBar.isTranslucent = true
searchBar.backgroundImage = UIImage.imageWithColor(color: UIColor.yellow, size: CGSize(width: searchBar.frame.width,height: searchBar.frame.height))
//modify textfield font and color attributes
let textFieldSearchBar = searchBar.value(forKey: SEARCH_BAR_SEARCH_FIELD_KEY) as? UITextField
textFieldSearchBar?.backgroundColor = UIColor.yellow
textFieldSearchBar?.font = UIFont(name: "Helvetica Neue" as String, size: 18)
textFieldSearchBar?.setValue(UIColor.yellow, forKeyPath: SEARCH_BAR_PLACEHOLDER_TEXT_KEY)
}
extension UIImage {
class func imageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect: CGRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
This achieves the UI as per your requirement, let me know if you need more customization on this. This is in swift 3 by the way.

Related

navigationBar color with alpha value

I want to change the color of my navigation bar when I scroll up. My scrollViewDidScroll looks like:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let safeArea: CGFloat = UIApplication.shared.windows.filter{$0.isKeyWindow}.first?.safeAreaInsets.top ?? 0
let alpha: CGFloat = ((scrollView.contentOffset.y + safeArea) / safeArea)
// This label becomes visible when scrolled up
navTitleLabel.alpha = alpha
self.navigationController?.navigationBar.barTintColor = .yellow.withAlphaComponent(alpha)
}
I even tried hardcoding 0 into .yellow.withAlphaComponent(alpha). But color is still visible. In case you wonder initial value (when not scrolled) of alpha, it is -0.9. How can I make navigation bar slowly visible as user scrolls, like navBarLabel.
Here is youtube link to the behaviour: https://youtu.be/75BjVK-nz4c
You can do this by generating an image from the yellow colour you’re using. Then on scrollView didScroll just set the navigations background image to be the image you generate.
extension UIColor {
func image(_ size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { rendererContext in
self.setFill()
rendererContext.fill(CGRect(origin: .zero, size: size))
}
}
}
navigationController?.navigationBar.setBackgroundImage(UIColor.orange.withAlphaComponent(alpha).image(),
for: .default)

How can I insert image to end of the string and insert more than one image in UITextView with swift?

I created an UITextView and I can add image with image picker into text view. But I have some problem about replacement image. I want add this image end of the text. And I want to add images more than one. (like: text + image + text...). How can I solve this problem ? Can anyone help me ?
let pickImage = UIImageView() // this is for imagepickercontroller
lazy var writePost: UITextView = {
let wpost = UITextView()
let images = pickImage
let attachment = NSTextAttachment()
let attString = NSAttributedString(attachment: attachment)
attachment.image = images.image
images.frame = CGRect(x: 0, y: 0, width: 220, height: 220)
images.contentMode = .scaleAspectFill
wpost.textStorage.insert(attString, at: wpost.selectedRange.location)
wpost.addSubview(images)
wpost.textAlignment = .center
wpost.textColor = UIColor.lightGray
wpost.font = UIFont(name: "AvenirNext-DemiBoldItalic", size: 16)
wpost.isEditable = true
wpost.isScrollEnabled = true
wpost.layer.borderWidth = 1.5
wpost.layer.cornerRadius = 7.0
wpost.layer.borderColor = UIColor.orange.cgColor
wpost.delegate = self
return wpost
}()
What you should do is use UITextView's textContainer's exclusionPaths property. The exclusionPaths property lets you assign an array of UIBezierPaths to your textContainer. When exclusionPaths are set, none of the UITextView's text will appear within these paths. You could then add a UIImageView as a subview of the UITextView's super view placed above the UITextView that has a frame equal to said exclusion path.
The end result will be a UITextView with a UIImageView placed above it. None of the UITextView's text will be blocked by the UIImageView as the UITextView's textContainer's exclusionPaths have instructed the text not to populate there.
Here is an example of some code I've done to do something similar, with variable names changed to match your code a bit:
let imageView: UIImageView!
func addImageView() {
imageView = UIImageView(frame: CGRect(x: textView.frame.maxX - 200, y: textView.frame.maxY - 150, width: 200, height: 150))
textView.superView.addSubview(imageView)
}
func setExclusionPath(for imageView: UIImageView) {
let imageViewPath = UIBezierPath(rect: CGRect(x: textView.frame.maxX - imageView.frame.width, y: textView.frame.maxY - imageView.frame.height, width: imageView.frame.width, height: imageView.frame.height))
textView.textContainer.exclusionPaths.append(imageViewPath)
}
func someMethod() {
addImageView()
setExclusionPath(for: self.imageView)
}
Resources:
exclusionPaths reference from Apple

Swift: Center NSAttributedString in View horizontally and vertically

Hi there,
I have a class CardButton: UIButton . In the draw method of this CardButton, I would like to add and center(vertically and horizontally) NSAttributed String, which is basically just one Emoji, inside of it. The result would look something like this:
However, NSAttributedString can be only aligned to center in horizontal dimension inside the container.
My idea for solution:
create a containerView inside of CardButton
center containerView both vertically and horizontally in it's container(which is CardButton)
add NSAttributedString inside the containerView and size containerView to fit the string's font.
So the result would look something like this:
My attempt for this to happen looks like this:
class CardButton: UIButton {
override func draw(){
//succesfully drawing the CardButton
let stringToDraw = attributedString(symbol, fontSize: symbolFontSize) //custom method to create attributed string
let containerView = UIView()
containerView.backgroundColor = //green
addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
let centerXConstraint = containerView.centerXAnchor.constraint(equalTo: (self.centerXAnchor)).isActive = true
let centerYConstraint = containerView.centerYAnchor.constraint(equalTo: (self.centerYAnchor)).isActive = true
stringToDraw.draw(in: containerView.bounds)
containerView.sizeToFit()
}
}
Long story short, I failed terribly. I first tried to add containerView to cardButton, made the background green, gave it fixed width and height jut to make sure that it got properly added as a subview. It did. But once I try to active constraints on it, it totally disappears.
Any idea how to approach this?
There is always more than one way to achieve any particular UI design goal, but the procedure below is relatively simple and has been adapted to suit the requirements as presented and understood in your question.
The UIButton.setImage method allows an image to be assigned to a UIButton without the need for creating a container explicitly.
The UIGraphicsImageRenderer method allows an image to be made from various components including NSAttributedText, and a host of custom shapes.
The process utilising these two tools to provide the rudiments for your project will be to:
Render an image with the appropriate components & size
Assign the rendered image to the button
A class could be created for this functionality, but that has not been explored here.
Additionally, your question mentions that when applying constraints the content disappears. This effect can be observed when image dimensions are too large for the container, constraints are positioning the content out of view and possibly a raft of other conditions.
The following code produces the above image:
func drawRectangleWithEmoji() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))
let img = renderer.image { (ctx) in
// Create the outer square:
var rectangle = CGRect(x: 0, y: 0, width: 512, height: 512).insetBy(dx: 7.5, dy: 7.5)
var roundedRect = UIBezierPath(roundedRect: rectangle, cornerRadius: 50).cgPath
// MARK: .cgPath creates a CG representation of the path
ctx.cgContext.setFillColor(UIColor.white.cgColor)
ctx.cgContext.setStrokeColor(UIColor.blue.cgColor)
ctx.cgContext.setLineWidth(15)
ctx.cgContext.addPath(roundedRect)
ctx.cgContext.drawPath(using: .fillStroke)
// Create the inner square:
rectangle = CGRect(x: 0, y: 0, width: 512, height: 512).insetBy(dx: 180, dy: 180)
roundedRect = UIBezierPath(roundedRect: rectangle, cornerRadius: 10).cgPath
ctx.cgContext.setFillColor(UIColor.green.cgColor)
ctx.cgContext.setStrokeColor(UIColor.green.cgColor)
ctx.cgContext.setLineWidth(15)
ctx.cgContext.addPath(roundedRect)
ctx.cgContext.drawPath(using: .fillStroke)
// Add emoji:
var fontSize: CGFloat = 144
var attrs: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: fontSize)//,
//.backgroundColor: UIColor.gray //uncomment to see emoji background bounds
]
var string = "❤️"
var attributedString = NSAttributedString(string: string, attributes: attrs)
let strWidth = attributedString.size().width
let strHeight = attributedString.size().height
attributedString.draw(at: CGPoint(x: 256 - strWidth / 2, y: 256 - strHeight / 2))
// Add NSAttributedString:
fontSize = 56
attrs = [
.font: UIFont.systemFont(ofSize: fontSize),
.foregroundColor: UIColor.brown
]
string = "NSAttributedString"
attributedString = NSAttributedString(string: string, attributes: attrs)
let textWidth = attributedString.size().width
let textHeight = attributedString.size().height
attributedString.draw(at: CGPoint(x: 256 - textWidth / 2, y: 384 - textHeight / 2))
}
return img
}
Activate the NSLayoutContraints and then the new image can be set for the button:
override func viewDidLoad() {
super.viewDidLoad()
view = UIView()
view.backgroundColor = .white
let buttonsView = UIView()
buttonsView.translatesAutoresizingMaskIntoConstraints = false
// buttonsView.backgroundColor = UIColor.gray
view.addSubview(buttonsView)
NSLayoutConstraint.activate([
buttonsView.widthAnchor.constraint(equalToConstant: CGFloat(buttonWidth)),
buttonsView.heightAnchor.constraint(equalToConstant: CGFloat(buttonWidth)),
buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
buttonsView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
let cardButton = UIButton(type: .custom)
cardButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
cardButton.setImage(drawRectangleWithEmoji(), for: .normal)
let frame = CGRect(x: 0, y: 0, width: buttonWidth, height: buttonWidth)
cardButton.frame = frame
buttonsView.addSubview(cardButton)
}
Your comments will be appreciated as would constructive review of the code provided.

why doesn't my UIImage appear in my tabBar?

I'm trying to add an UIImage to customise my tabBar just like I did with my navigationBar, but it seems not work.
First I create a CAGradientLayer and then I turn it into UIImage. Here is the code:
extension CALayer {
func createImageFromLayer() -> UIImage?{
frame = CGRect(x: 0, y: 0, width: 1000, height: 1000)
UIGraphicsBeginImageContext(frame.size)
guard let context = UIGraphicsGetCurrentContext() else {return nil}
render(in: context)
let output = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return output
}
}
class CustomTabBarController: UITabBarController {
let grad: CAGradientLayer = {
let gradient = CAGradientLayer()
gradient.colors = [UIColor.clear, UIColor.black]
gradient.locations = [0,1]
return gradient
}()
override func viewDidLoad() {
super.viewDidLoad()
let tabImage = grad.createImageFromLayer()
self.tabBar.isTranslucent = true
self.tabBar.backgroundImage = tabImage
self.tabBar.shadowImage = UIImage()
}
}
Setting an image for my navigationBar worked perfectly using this method:
navigationController?.navigationBar.setBackgroundImage(barImg, for: UIBarMetrics.default)
In this code barImg is UIImage created from a CAGradientLayer just like with tabBar. I've checked if tabImage is nil and it's not.
It seems like tabBar doesn't have a function like tabBar.setBackgroundImage like navigationBar. How do I go about doing this?
The only result I get is a completely clear background for my tabBar with no gradient added and I don't know what I'm missing here once tabBar.backgroundImage doesn't work.
Thank you for the answers.
At first, when you setup colors for a CAGradientLayer you should use CGColor objects only. So you need to change that line to
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
After that your tabImage won't be empty and become visible.
The last problem is that you always set your image size to 1000x1000 pixels. That's way too much. To fix that you can pass target frame size to createImageFromLayer as a parameter. Its signature will become something like this.
func createImageFromLayer(targetFrame: CGRect) -> UIImage?
In your case targetFrame is tabBar.bounds.

swift textfield bottom line max width

I wrote the below code to make the textfield only have bottom line,
the result show as the below image (the bottom line is not long enough to the right edge.)
func setBottomBorder() {
self.borderStyle = .none
self.layer.backgroundColor = UIColor.clear.cgColor
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes: [NSAttributedStringKey.foregroundColor: UIColor.white])
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.white.cgColor
border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
border.borderWidth = width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
}
As per My suggestion instead of progrmatically.
you should set it using StoryBoard:
A textfield with Hidden Border.
Below that you can take a label with white background and it is done.
Note:- Do not forget to give correct constraints to both so that it looks good