CoreGraphice Stroke Text with Gradient - macos

With the help of CGContextSetTextDrawingMode we are able to draw bordered Text. How can we fill the border with a brush (Gradient/Picture) instead of a solid color?
Can't find this on Apple Docs.

by using context.addPath(path) , then context.clip(to: pathFrame)
then you can use context.drawLinearGradient
in order to draw without side effects,
remember to use context.saveGState() and context.restoreGState(),
Here is an example for iOS version,
you should edit it to MacOS version
class CustomV: UIView{
override func draw(_ rect: CGRect) {
// Create a gradient from white to red
let colors: [CGFloat] = [
1.0, 1.0, 1.0, 1.0,
1.0, 0.0, 0.0, 1.0]
let baseSpace = CGColorSpaceCreateDeviceRGB()
guard let context = UIGraphicsGetCurrentContext(), let gradient =
CGGradient(colorSpace: baseSpace, colorComponents: colors, locations: nil, count: 2) else{ return }
context.saveGState()
let offset: CGFloat = 10
let startPoint = CGPoint(x: rect.maxX - offset, y: rect.minY)
let endPoint = CGPoint(x: rect.maxX - offset, y: rect.maxY)
let p = CGMutablePath()
p.move(to: startPoint)
p.addLine(to: endPoint)
context.addPath(p)
context.clip(to: CGRect(origin: startPoint, size: CGSize(width: 3, height: 800)))
context.setLineWidth(offset)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
context.restoreGState()
}
}

Related

How to add corner smoothing to UIImageView?

I have an image with which I perform editing actions
now I'm trying to make the logic of smoothing the edges just for the picture, not for the imageView
the user must pass a value with which the edges of the image should be smoothed
I tried to use this code but it doesn't work for me
func image(withRoundedCornersRadius radius: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIBezierPath(roundedRect: rect, cornerRadius: CGFloat(radius)).addClip()
draw(in: rect)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage
}

Programmatically overlay image on gradient

I’m building out a UI that has a few screens with a gradient background and a tiled PNG (blend mode: multiply # 9% opacity) on top to create noise.
I've managed to create the gradient programatically with this snippet I found here but I'm struggling to figure out how to edit this to add the PNG over it
import Foundation
import UIKit
#IBDesignable class DarkGradient: UIView {
let titleLabel = UILabel()
#IBInspectable var title: String? = nil {
didSet {
titleLabel.text = title
}
}
override func layoutSubviews() {
super.layoutSubviews()
// Necessary in layoutSubviews to get the proper frame size.
configure()
}
func configure() {
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor(red: 0.16, green: 0.0, blue: 0.54, alpha: 1).cgColor,
UIColor(red: 0.26, green: 0.0, blue: 0.88, alpha: 1).cgColor]
gradient.startPoint = CGPoint(x: -0.5, y: 0.5)
gradient.endPoint = CGPoint(x: 0.5, y: 1.5)
layer.insertSublayer(gradient, at: 0)
}
}
Grateful for any help, all the answers I've found have been overlaying gradients over images.

animation not working in xcode 8 playground swift 3

I am trying to simply learn more about animations in Xcode 8 playground using swift 3. I have already learned that swift no longer uses XCPlayground, instead, using "import PlaygroundSupport".
I can not get anything to show in the Assistant Editor. Does anyone know the fix for this or is it just a bug with the new Xcode 8
Any help would be awesome. Thanks.
import UIKit
import PlaygroundSupport
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 375.0, height: 667.0))
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = containerView
let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
circle.center = containerView.center
circle.layer.cornerRadius = 25.0
let startingColor = UIColor(red: (253.0/255.0), green: (159.0/255.0), blue: (47.0/255.0), alpha: 1.0)
circle.backgroundColor = startingColor
containerView.addSubview(circle);
let rectangle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
rectangle.center = containerView.center
rectangle.layer.cornerRadius = 5.0
rectangle.backgroundColor = UIColor.white
containerView.addSubview(rectangle)
UIView.animate(withDuration: 2.0, animations: { () -> Void in
let endingColor = UIColor(red: (255.0/255.0), green: (61.0/255.0), blue: (24.0/255.0), alpha: 1.0)
circle.backgroundColor = endingColor
let scaleTransform = CGAffineTransform(scaleX: 5.0, y: 5.0)
circle.transform = scaleTransform
let rotationTransform = CGAffineTransform(rotationAngle: 3.14)
rectangle.transform = rotationTransform
})
Open the assistant editor to view the live view of the animation as mentioned in the article you're basing this off

iOS10 - can't render Sprite Kit scene within SceneKit with openGL

Since I've updated to iOS 10, I'm not able anymore to render a Sprite Kit scene to a Scene Node while using openGL for rendering. Things work fine with Metal. The error logs: "Failed to create IOSurface image (texture)"
I used to be able to do something like:
class ViewController: UIViewController {
#IBOutlet weak var scnView: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
scnView.showsStatistics = true
scnView.allowsCameraControl = true
let scnScene = SCNScene()
scnView.scene = scnScene
print("scnView renderingAPI is metal", scnView.renderingAPI == SCNRenderingAPI.metal)
print("scnView renderingAPI is opengl", scnView.renderingAPI == SCNRenderingAPI.openGLES2)
// setup SceneKit scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 25.0)
scnScene.rootNode.addChildNode(cameraNode)
let cubeNode = SCNNode()
cubeNode.geometry = SCNBox(width: 5.0, height: 5.0, length: 5.0, chamferRadius: 0.0)
scnScene.rootNode.addChildNode(cubeNode)
// setup SpriteKit Scene
let skScene = SKScene()
skScene.backgroundColor = UIColor.black
skScene.size = CGSize(width: 100, height: 100)
let skNode = SKShapeNode(rect: CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0))
skNode.fillColor = UIColor.green
skNode.position = CGPoint(x: 5.0, y: 5.0)
skScene.addChild(skNode)
cubeNode.geometry?.firstMaterial?.diffuse.contents = skScene
}
}
(here is a repo to reproduce this: https://github.com/gsabran/iOS10-OpenGLRenderingBug)

Text Field with Only top and Bottom Border using swift 2

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()
}

Resources