skew UIView without giving perspective and keeping heigh and width
Asked Answered
V

2

5

Skew Example Hi,

I am trying to achieve a transformation like in the image but I only have got a similar transformation but with perspective (which I don't want) using CGATransform3D. How could I achieve this using objective-c? The transformed image should have the same heigh and width than the original but with the base tilted by a given angle.

Thanks in advance for your help.

Villainy answered 2/3, 2016 at 21:55 Comment(1)
You can use Core Image for that.Headphone
C
16

This is a “shear” or “skew” transformation. Luckily for you, shear transformations are affine transformations, and iOS supports affine transformations at both the UIView and CALayer levels. However, it has no convenience functions to construct shear transforms, so let's write one:

static CGAffineTransform affineTransformMakeShear(CGFloat xShear, CGFloat yShear) {
    return CGAffineTransformMake(1, yShear, xShear, 1, 0, 0);
}

It's not obvious exactly what xShear and yShear mean. xShear is the tangent of the angle by which the y-axis is skewed, and yShear is the tangent of the angle by which the x-axis is skewed. This may seem backwards, but it has the visual effect you expect: a non-zero xShear tilts the figure left or right, and a non-zero yShear tilts the figure up or down.

Since tan π/4 = 1 (or, if you prefer, tan 45° = 1), the transform returned by affineTransformMakeShear(1, 0) turns a rectangle into a parallelogram whose left and right edges are at π/4 radians (or 45°).

Demo:

shear demo

Note that shearing on both axes can be identical to the combination of a rotation and a scale.

Here's my demo code:

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) IBOutlet UIView *shearedView;
@property (strong, nonatomic) IBOutlet UISlider *verticalShearSlider;
@property (strong, nonatomic) IBOutlet UISlider *horizontalShearSlider;
@property (strong, nonatomic) IBOutlet UILabel *verticalShearLabel;
@property (strong, nonatomic) IBOutlet UILabel *horizontalShearLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateAppearance:nil];
}

static CGAffineTransform affineTransformMakeShear(CGFloat xShear, CGFloat yShear) {
    return CGAffineTransformMake(1, yShear, xShear, 1, 0, 0);
}

- (IBAction)updateAppearance:(id)sender {
    CGFloat xShear = self.horizontalShearSlider.value;
    CGFloat yShear = self.verticalShearSlider.value;
    self.shearedView.transform = affineTransformMakeShear(xShear, yShear);
    self.horizontalShearLabel.text = [NSString stringWithFormat:@"%.2f", xShear];
    self.verticalShearLabel.text = [NSString stringWithFormat:@"%.2f", yShear];
}

@end
Colourable answered 2/3, 2016 at 22:21 Comment(8)
Thanks a lot!! this is awesome and easier than i thought! :DVillainy
@rob you have the best demos dude :) it would be cool if someone changed these classics to Swift one day...Beamy
@rob mayoff i still don't understand, how to provide angle value in radians or degrees?Dorman
Pass tan(angle) to affineTransformMakeShear.Colourable
Interesting note: while UIView is being transformed based off its center, CGRect is transformed based of its top left vertex.Kaja
What has affineTransformMakeShear been renamed in Swift 6? The compiler says No ;-) and can't find any references.Vacla
There is no Swift 6 yet (and probably won't be for the next several years). Also, affineTransformMakeShear is a function defined in this answer. It has never been part of the iOS SDK.Colourable
Sorry, I wasn't trying to humour you. We're on 5.5.1 so there is hope. And apologies for not reading the opening bit of your exceptionally well documented answer.Vacla
A
0

@Fattie. Quick Swift version of Rob's demo:

class AffineTester: UIViewController
{
    @IBOutlet weak var shearedView: UIView!
    @IBOutlet weak var verticalShearSlider: UISlider!
    @IBOutlet weak var horizontalShearSlider: UISlider!
    @IBOutlet weak var verticalShearLabel: UILabel!
    @IBOutlet weak var horizontalShearLabel: UILabel!

    override func viewDidLoad()
    {
        super.viewDidLoad()
        updateAppearance(self)
    }
    
    @IBAction func updateAppearance(_ sender: Any)
    {
        let xShear = CGFloat(horizontalShearSlider.value)
        let yShear = CGFloat(verticalShearSlider.value)
        shearedView.transform = CGAffineTransform(1, yShear, xShear, 1, 0, 0)
        horizontalShearLabel.text = String(format: "%.2f", xShear)
        verticalShearLabel.text = String(format: "%.2f", yShear)
    }
}
Awful answered 29/10, 2022 at 2:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.