I am trying to make the background color of a UItextfield blurry. When I try the code below, my app crashes when it runs. Has anyone tried this before and knows how to make a UITextfield blurry?
let p = UITextField()
let blurEffect = UIBlurEffect(style: .light)
let blurView = UIVisualEffectView(effect: blurEffect)
p.layer.isOpaque = true
p.layer.backgroundColor = blurView as! CGColor
I found a solution where you place a view behind the UITextfield, and make it transparent.
let v = UIView()
v.frame = CGRect(x: 30, y: 100, width: 180, height: 30)
let blurEffect = UIBlurEffect(style: .light)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = v.bounds
blurView.backgroundColor = .clear
v.addSubview(blurView)
let p = UITextField()
p.frame = CGRect(x: 0, y: 0, width: 180, height: 30)
p.layer.isOpaque = true
p.backgroundColor = .clear
v.addSubview(p)
self.view.backgroundColor = .red
self.view.addSubview(v)
This is an example of proposed solution, with background image instead of red color, to emphasize the blur effect
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background") ?? UIImage())
Related
The code below creates a red rectangle that is animated to move across the view from left to right. I would like to have an arbitrary shape loaded from an image to either superimpose or replace the rectangle. However, the circleLayer.contents = NSImage statement in the initializeCircleLayer function doesn't produce any effect. The diagnostic print statement seems to verify that the image exists and has been found, but no image appears in the view. How do I get an image into the layer to replace the animated red rectangle? Thanks!
CODE BELOW:
import Cocoa
class ViewController: NSViewController {
var circleLayer = CALayer()
override func viewDidLoad() {
super.viewDidLoad()
self.view.wantsLayer = true
initializeCircleLayer()
simpleCAAnimationDemo()
}
func initializeCircleLayer(){
circleLayer.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
circleLayer.position = CGPoint(x: 50, y: 150)
circleLayer.backgroundColor = NSColor.red.cgColor
circleLayer.cornerRadius = 10.0
let testIm = NSImage(named: NSImage.Name(rawValue: "testImage"))
print("testIm = \(String(describing: testIm))")
circleLayer.contents = NSImage(named: NSImage.Name(rawValue: "testImage"))?.cgImage
circleLayer.contentsGravity = kCAGravityCenter
self.view.layer?.addSublayer(circleLayer)
}
func simpleCAAnimationDemo(){
circleLayer.removeAllAnimations()
let animation = CABasicAnimation(keyPath: "position")
let startingPoint = NSValue(point: NSPoint(x: 50, y: 150))
let endingPoint = NSValue(point: NSPoint(x: 600, y: 150))
animation.fromValue = startingPoint
animation.toValue = endingPoint
animation.repeatCount = Float.greatestFiniteMagnitude
animation.duration = 10.0
circleLayer.add(animation, forKey: "linearMovement")
}
}
Why it doesn't work
The reason why
circleLayer.contents = NSImage(named: NSImage.Name(rawValue: "testImage"))?.cgImage
doesn't work is because it's a reference to the cgImage(forProposedRect:context:hints:) method, meaning that its type is
((UnsafeMutablePointer<NSRect>?, NSGraphicsContext?, [NSImageRep.HintKey : Any]?) -> CGImage?)?
You can see this by assigning NSImage(named: NSImage.Name(rawValue: "testImage"))?.cgImage to a local variable and ⌥-clicking it to see its type.
The compiler allows this assignment because circleLayer.contents is an Any? property, so literally anything can be assigned to it.
How to fix it
As of macOS 10.6, you can assign NSImage objects to a layers contents directly:
circleLayer.contents = NSImage(named: NSImage.Name(rawValue: "testImage"))
I have migrated my app from Swift 3.1 to Swift 4.0 (Xcode 8.3.3 to Xcode 9.0) and some part of my interface is broken now. Navigation Bar of Navigation controller is complete mess. Please, look at screenshot:
There are 3 elements:
left Netfnet logo (image)
right Signal strength (image)
right QR Code button
As you can see, two images are too big and not in center and button was deformed (it should be perfect square, all images too).
There is code which generated navigation controller:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
settings()
}
func settings() {
let color = UIColor(red: 81 / 255, green: 155 / 255, blue: 22 / 255, alpha: 1.0)
self.navigationController?.navigationBar.barTintColor = color
let logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
logoImageView.contentMode = .scaleAspectFit
let logo = UIImage(named: "littleLogoImage")
logoImageView.image = logo
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: logoImageView)
let signalStengthImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
signalStengthImageView.contentMode = .scaleAspectFit
signalStengthImageView.image = UIImage(named: "signalStrength4")
let signalStengthImageItem = UIBarButtonItem(customView: signalStengthImageView)
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "qrCodeButton"), for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
let qrCodeButtonItem = UIBarButtonItem(customView: button)
navigationItem.rightBarButtonItems = [qrCodeButtonItem, signalStengthImageItem] //
}
}
I can decrease resolution of images directly myself, but I just don't get why everting was fine in Swift 3.1 and in Swift 4.0 is broken.
I will be thankful for any help or advice.
You have to add width and height constraints.
Your barImageView and barButton in CustomNavigationController should be like below :
func barImageView(imageName: String) -> UIBarButtonItem {
let imgView = imageView(imageName: imageName)
let widthConstraint = imgView.widthAnchor.constraint(equalToConstant: 35)
let heightConstraint = imgView.heightAnchor.constraint(equalToConstant: 35)
heightConstraint.isActive = true
widthConstraint.isActive = true
return UIBarButtonItem(customView: imgView)
}
func barButton(imageName: String, selector: Selector) -> UIBarButtonItem {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: imageName), for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
button.addTarget(self, action: selector, for: .touchUpInside)
let widthConstraint = button.widthAnchor.constraint(equalToConstant: 35)
let heightConstraint = button.heightAnchor.constraint(equalToConstant: 35)
heightConstraint.isActive = true
widthConstraint.isActive = true
return UIBarButtonItem(customView: button)
}
Your signalStengthImageView in LogoWithSignalStrength:
signalStengthImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
signalStengthImageView.contentMode = .scaleAspectFit
let widthConstraint = signalStengthImageView.widthAnchor.constraint(equalToConstant: 35)
let heightConstraint = signalStengthImageView.heightAnchor.constraint(equalToConstant: 35)
heightConstraint.isActive = true
widthConstraint.isActive = true
In Xcode 9, Navigation bar items are constraints base, Add this:
if #available(iOS 11.0, *) {
logoImageView.widthAnchor.constraint(equalToConstant: 35).isActive = true
logoImageView.heightAnchor.constraint(equalToConstant: 35).isActive = true
} else {
//set frames
}
Image of the pixelated text in the UITextView
Does anyone have any suggestions? The image of the issue is in the clickable link above.
Code:
struct Views {
static var name_field: UITextView?
}
In the viewDidLoad()
Views.name_field = UITextView(frame: CGRectMake(0, 0, name_field_width, 50))
Views.name_field!.textAlignment = NSTextAlignment.Center
Views.name_field!.font = UIFont.systemFontOfSize(15)
Views.name_field!.autocorrectionType = UITextAutocorrectionType.No
Views.name_field!.keyboardType = UIKeyboardType.Default
Views.name_field!.returnKeyType = .Done
Views.name_field!.delegate = self
Calling this function to style it
styleIt(Views.name_field!)
Adds a bottom border style and then sets the font, etc.
func styleIt(target: UITextView){
target.layer.backgroundColor = UIColor.clearColor().CGColor
let _border = CAShapeLayer()
_border.backgroundColor = UIColor.whiteColor().CGColor
_border.frame = CGRectMake(0, CGRectGetHeight(target.frame) - 1.0, CGRectGetWidth(target.frame), 1.0)
_border.shadowColor = UIColor.whiteColor().CGColor
_border.shadowOffset = CGSize(width: 3, height: 3)
_border.shadowOpacity = 0.23
_border.shadowRadius = 4
target.layer.addSublayer(_border)
target.font = UIFont(name: "ClementePDaa-Hairline", size: 24)
target.textColor = UIColor.whiteColor()
target.textContainerInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
applyPlaceholderStyle(target, placeholderText: _SEARCH_TEXT)
target.returnKeyType = .Done
target.frame = CGRectIntegral(target.frame)
target.layer.shouldRasterize = true
_border.shouldRasterize = true
target.textInputView.layer.shouldRasterize = true
}
This UITextView is a subview of search_field which is simply a UIView
search_field!.addSubview(Views.name_field!)
Your text view is blurry because the frame is using floating numbers.
To force integers value for your frame just do :
textView.frame = CGRectIntegral(textView.frame)
I am trying to insert a colored line under one of my labels :
let label = UILabel(frame: CGRectMake(0, 0, 70, 40))
label.text = items[index - 1]
label.backgroundColor = UIColor.clearColor()
label.textAlignment = .Center
label.font = UIFont(name: "Helvetica", size: 15)
label.textColor = index == 1 ? selectedLabelColor : unselectedLabelColor
label.translatesAutoresizingMaskIntoConstraints = false
var sublayer = label.layer;
sublayer.backgroundColor = UIColor.blueColor().CGColor
sublayer.frame = CGRectMake(0, 0, label.frame.width, 1);
sublayer.borderColor = UIColor.blackColor().CGColor;
sublayer.borderWidth = 1;
self.layer.insertSublayer(sublayer, atIndex: 0)
self.addSubview(label)
How can I set the frame properly so that there is a colored line under my label ?
Ended up using a uiview :
let lineView = UIView(frame: CGRectMake(0,
self.frame.height - 3,
label.frame.width,
3.0))
lineView.backgroundColor = UIColor.blackColor()
self.addSubview(lineView)
Works really well.
Swift 3:
let lineView = UIView(frame: CGRect(x: 0, y: self.frame.height - 3, width: frame.width, height: 3.0))
lineView.backgroundColor = UIColor.black
self.addSubview(lineView)
I have a page for forgot password. It has only a text field asking the user to fill in their email address. The Designer designed the text field with top and bottom border only.
I tried answer from here UITextField Only Top And Bottom Border
but in the result it only shows bottom border for the text field.
Like in the image i would like to create a grey border for top and bottom
To remove Fights with views you could create a tableView with a static cell that contains a TextField. Voila done... Top and bottom border comes for free and you will use standard apple stuff :)
If you really want to draw the layers than follow the steps on your linked questions:
CALayer *topBorder = [CALayer layer];
topBorder.frame = CGRectMake(0, 0, self.frame.size.width, 1);
topBorder.backgroundColor = [UIColor blackColor].CGColor;
[myTextField.layer addSublayer:topBorder];
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0, self.frame.size.height - 1, self.frame.size.width, 1);
bottomBorder.backgroundColor = [UIColor blackColor].CGColor;
[myTextField.layer addSublayer:bottomBorder];
In Swift:
let topBorder = CALayer()
topBorder.frame = CGRectMake(0, 0, bounds.size.width, 1)
topBorder.backgroundColor = UIColor.grayColor()
textField.layer.addSublayer(topBorder)
let bottomBorder = CALayer()
bottomBorder.frame = CGRectMake(0, bounds.size.height-1, bounds.size.width, 1)
bottomBorder.backgroundColor = UIColor.grayColor()
textField.layer.addSublayer(bottomBorder)
Thanks #El Captain for the valuable comment and nice answer by #Bjorn Ro even if it was in Objective-c i think.
And my answer for the question is (i'm using swift 2 Xcode 7)
Override the function viewDidLayoutSubviews() in your swift file. And the Code for the same is
override func viewDidLayoutSubviews() {
// Creates the bottom border
let borderBottom = CALayer()
let borderWidth = CGFloat(2.0)
borderBottom.borderColor = UIColor.grayColor().CGColor
borderBottom.frame = CGRect(x: 0, y: forgotPasswordEmailText.frame.height - 1.0, width: forgotPasswordEmailText.frame.width , height: forgotPasswordEmailText.frame.height - 1.0)
borderBottom.borderWidth = borderWidth
forgotPasswordEmailText.layer.addSublayer(borderBottom)
forgotPasswordEmailText.layer.masksToBounds = true
// Creates the Top border
let borderTop = CALayer()
borderTop.borderColor = UIColor.grayColor().CGColor
borderTop.frame = CGRect(x: 0, y: 0, width: forgotPasswordEmailText.frame.width, height: 1)
borderTop.borderWidth = borderWidth
forgotPasswordEmailText.layer.addSublayer(borderTop)
forgotPasswordEmailText.layer.masksToBounds = true
}
forgotPasswordEmailText is the text field for entering Email
The Final output looks like this... with a gray Colour border (Screen shot of iPhone 4s Simulator)
Good suggestions for programatic solution posted so far. But I figured I'd share an Interfacebuilder solution....
1) Create view collection in your view controller
#IBOutlet private var borderViews: [UIView]?
2) Create 2 UIViews in interface builder 1px high constrained to where you want them around the textfield
3) Connect the 2 views in interface builder to borderViews IBOutlet
4) Customise appearance of both views by using setValue forKeyPath... for example, on success you may want the border to turn green
setValue(UIColor.green, forKeyPath: "borderViews.backgroundColor")
In Swift 3 use extension:
Create Swift file
import UIKit
extension UITextField {
func setBottomBorder() {
self.borderStyle = .none
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
self.layer.shadowOpacity = 1.0
self.layer.shadowRadius = 0.0
}
}
Call from anywhere:
PasswordField.setBottomBorder();
Here's a nice and easy Swift 4 implementation that handles resizing views as well :)
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
viewToShadow.backgroundColor = .white
viewToShadow.layer.masksToBounds = false
viewToShadow.layer.sublayers?
.filter { layer -> Bool in
return layer.backgroundColor == UIColor.almostBlack.alpha(0.5).cgColor
}
.forEach { layer in
layer.removeFromSuperlayer()
}
[CGRect(x: 0.0, y: 0.0, width: viewToShadow.bounds.width, height: 0.5),
CGRect(x: 0.0, y: viewToShadow.bounds.height, width: viewToShadow.bounds.width, height: 0.5)]
.forEach { frame in
let layer = CALayer()
layer.frame = frame
layer.backgroundColor = UIColor.almostBlack.alpha(0.5).cgColor
viewToShadow.layer.addSublayer(layer)
}
}
Use handy extension for it
extension UITextField {
func addTopBorder(){
let bottomLine = CALayer()
bottomLine.frame = CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: 1)
bottomLine.backgroundColor = UIColor.white.cgColor
self.borderStyle = UITextField.BorderStyle.none
self.layer.addSublayer(bottomLine)
}
func addBottomBorder(){
let bottomLine = CALayer()
bottomLine.frame = CGRect.init(x: 0, y: self.frame.size.height - 1, width: self.frame.size.width, height: 1)
bottomLine.backgroundColor = UIColor.white.cgColor
self.attributedPlaceholder = NSAttributedString(string: self.placeholder ?? "-", attributes: [NSAttributedString.Key.foregroundColor : #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)])
self.borderStyle = UITextField.BorderStyle.none
self.layer.addSublayer(bottomLine)
}
}
use it in you controller like this
yourTextfield.addTopBorder()
yourTextfield.addBottomBorder()
and don't forget to use it on main thread
DispatchQueue.main.async {
self.yourTextfield.addTopBorder()
self.yourTextfield.addBottomBorder()
}