9

I need to create a UIView that has the left border inclined with 45 degrees I was wondering,is there a way to acheive this programmatically? Does CATransform3D help me in this case since it’s not really a “3D rotation”?

Edit

Here's an image explaining more my needed output

enter image description here

Odin
  • 642
  • 1
  • 9
  • 27
  • This could be interpreted many ways. Do you have a picture of what you want. This could be a right angled triangle, or a rhombus, etc... All possible programatically without using transforms but we'll need a picture (or a better explanation) to be able to answer. – Fogmeister Oct 13 '14 at 12:31
  • 1
    Using a transform will distort the contents of your view, and probably isn't what you want. – James Snook Oct 13 '14 at 12:33
  • @JamesSnook This UIView will not have content.. it's only for an overlay.. – Odin Oct 13 '14 at 12:38
  • I guess subclassing `UIView` and overriding `drawRect` is the best approach to your issue... – nburk Oct 13 '14 at 12:47
  • This is doable with a transform, but it isn't trivial. If one corner of your rect is at the origin then you can apply a shear transform to either of the edges that don't run along the origin. After this you can rotate and translate the view to the correct orientation using a couple more transforms. You can concatenate all of these into one transform using matrix arithmetic (or get the computer to do it for you). I think you're better off using drawRect! – James Snook Oct 13 '14 at 12:50

4 Answers4

12

If you just want the shape with no content then you can create a CAShapeLayer and add it to your view's layer. (In fact you can also put content in there using this method but you'll need to alter it a bit).

CAShapeLayer *layer = [CAShapeLayer layer];

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 100)]; // bottom left corner
[path addLineToPoint:CGPointMake(100, 0)]; // top middle
[path addLineToPoint:CGPointMake(300, 0)]; // top right corner
[path addLineToPoint:CGPointMake(300, 100)]; // bottom right corner
[path closePath];

layer.path = path.CGPath;
layer.fillColor = [UIColor blackColor].CGColor;
layer.strokeColor = nil;

[theView.layer addSubLayer:layer];

This doesn't require using drawRect or anything. You will have to change the coordinates based on the values you want.

You can also use a UIView subclass and override drawRect. It requires more work but the UIBezierPath will be pretty much the same.

CALayer is very powerful and used a lot by Apple. For instance the edit canvas in Pages is written almost exclusively using CALayers.

aguilarpgc
  • 1,181
  • 12
  • 24
Fogmeister
  • 76,236
  • 42
  • 207
  • 306
1

Resulted shape

Code in Swift5

    -

 class CustomView: UIView {

// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
    // Drawing code
    // Get Height and Width
    //let layerHeight = layer.frame.height
    let layerWidth = layer.frame.width
    // Create Path
    let bezierPath = UIBezierPath()
    //  Points
    let pointA = CGPoint(x: 0, y: 30)
    let pointB = CGPoint(x: 20, y: 0)
    let pointC = CGPoint(x: layerWidth, y: 0)
    let pointD = CGPoint(x: layerWidth, y:30)
    // Draw the path
    bezierPath.move(to: pointA)
    bezierPath.addLine(to: pointB)
    bezierPath.addLine(to: pointC)
    bezierPath.addLine(to: pointD)
    bezierPath.close()
    // Mask to Path
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = bezierPath.cgPath
    layer.mask = shapeLayer
}
}

0

Shortest way to achieve this is probably to take the following steps:

  1. Subclass UIView
  2. Override drawRect to provide your custom drawing for the view
  3. In drawRect, use the Core Graphics API, which allows you to draw custom shapes on a drawing context, and offers functions such as rotation or scaling

Here is an overview of the API from the Apple docs.

nburk
  • 22,409
  • 18
  • 87
  • 132
0

I wanted to provide a code sample using Swift 5 on how to implement it in a UIViewController all using programmatic UI with UIKit. The key component for me that I missed was that where you create a CAShapeLayer and add it to a view's layer, it must be called in either layoutSubviews() if you're doing in a UIView or viewDidLayoutSubviews() if you're creating the shape within the UIViewController. You'll need to familiarize yourself with how to use UIBezierPath to understand the drawing mechanics.

Here are the resources I found to be most useful:

Apple Docs on UIBezierPath

Medium Blog write up on UIBezierPath

import UIKit

class ViewController: UIViewController {
    var diagonalSideView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .black
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        makeDiagonalDividerShape(from: diagonalSideView)
    }

    private func setupViews() {
        view.addSubview(diagonalSideView)

        let heightConstraint = diagonalSideView.heightAnchor.constraint(equalToConstant: 200)
        let leftConstraint = diagonalSideView.leftAnchor.constraint(equalTo: view.leftAnchor)
        let rightConstraint = diagonalSideView.rightAnchor.constraint(equalTo: view.rightAnchor)
        let centerXConstraint = diagonalSideView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        let centerYConstraint = diagonalSideView.centerYAnchor.constraint(equalTo: view.centerYAnchor)

        NSLayoutConstraint.activate([heightConstraint,
                                     leftConstraint,
                                     rightConstraint,
                                     centerXConstraint,
                                     centerYConstraint])
    }

    private func makeDiagonalDividerShape(from view: UIView) {
        let layer = CAShapeLayer()

        // Create Path
        let bezierPath = UIBezierPath()

        //  Points of the Polygon with Diagonal left side
        let pointA = CGPoint(x: view.bounds.origin.x, y: view.bounds.height)
        let pointB = CGPoint(x: view.bounds.width / 4, y: view.bounds.origin.y)
        let pointC = CGPoint(x: view.bounds.width, y: view.bounds.origin.y)
        let pointD = CGPoint(x: view.bounds.width, y: view.bounds.height)

        // Draw the path
        bezierPath.move(to: pointA)
        bezierPath.addLine(to: pointB)
        bezierPath.addLine(to: pointC)
        bezierPath.addLine(to: pointD)
        bezierPath.close()

        // Create the mask
        layer.path = bezierPath.cgPath
        view.layer.mask = layer
    }
}

Final result:

Final result

David Shi
  • 186
  • 3
  • 3