历史版本
版本号 | 时间 |
---|---|
V1.0 | 2017.07.12 |
前言
大家在做图片上传还有头像等很多时候,经常都会碰到图片拉伸或者压缩等几种情况,下面我们就举个例子说一下这几种填充模式。
一、代码展示
下面我们就先写一个简单的控件,并渲染出来如下所示。
先看一下下面的代码。
#import "JJContentModeVC.h"
@interface JJContentModeVC ()
@property (nonatomic, strong) UIImageView *coverImageView;
@end
@implementation JJContentModeVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self setupUI];
}
#pragma mark - Object Private Function
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
@end
下面我们先看一下原图
下面看一下仿真器效果图。
二、几种内容模式基础
下面我们看一下苹果的API,如下所示。
@interface UIView(UIViewRendering)
- (void)drawRect:(CGRect)rect;
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)rect;
@property(nonatomic) BOOL clipsToBounds; // When YES, content and subviews are clipped to the bounds of the view. Default is NO.
@property(nullable, nonatomic,copy) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is nil. Can be useful with the appearance proxy on custom UIView subclasses.
@property(nonatomic) CGFloat alpha; // animatable. default is 1.0
@property(nonatomic,getter=isOpaque) BOOL opaque; // default is YES. opaque views must fill their entire bounds or the results are undefined. the active CGContext in drawRect: will not have been cleared and may have non-zeroed pixels
@property(nonatomic) BOOL clearsContextBeforeDrawing; // default is YES. ignored for opaque views. for non-opaque views causes the active CGContext in drawRect: to be pre-filled with transparent pixels
@property(nonatomic,getter=isHidden) BOOL hidden; // default is NO. doesn't check superviews
@property(nonatomic) UIViewContentMode contentMode; // default is UIViewContentModeScaleToFill
@property(nonatomic) CGRect contentStretch NS_DEPRECATED_IOS(3_0,6_0) __TVOS_PROHIBITED; // animatable. default is unit rectangle {{0,0} {1,1}}. Now deprecated: please use -[UIImage resizableImageWithCapInsets:] to achieve the same effect.
@property(nullable, nonatomic,strong) UIView *maskView NS_AVAILABLE_IOS(8_0);
/*
-tintColor always returns a color. The color returned is the first non-default value in the receiver's superview chain (starting with itself).
If no non-default value is found, a system-defined color is returned.
If this view's -tintAdjustmentMode returns Dimmed, then the color that is returned for -tintColor will automatically be dimmed.
If your view subclass uses tintColor in its rendering, override -tintColorDidChange in order to refresh the rendering if the color changes.
*/
@property(null_resettable, nonatomic, strong) UIColor *tintColor NS_AVAILABLE_IOS(7_0);
/*
-tintAdjustmentMode always returns either UIViewTintAdjustmentModeNormal or UIViewTintAdjustmentModeDimmed. The value returned is the first non-default value in the receiver's superview chain (starting with itself).
If no non-default value is found, UIViewTintAdjustmentModeNormal is returned.
When tintAdjustmentMode has a value of UIViewTintAdjustmentModeDimmed for a view, the color it returns from tintColor will be modified to give a dimmed appearance.
When the tintAdjustmentMode of a view changes (either the view's value changing or by one of its superview's values changing), -tintColorDidChange will be called to allow the view to refresh its rendering.
*/
@property(nonatomic) UIViewTintAdjustmentMode tintAdjustmentMode NS_AVAILABLE_IOS(7_0);
/*
The -tintColorDidChange message is sent to appropriate subviews of a view when its tintColor is changed by client code or to subviews in the view hierarchy of a view whose tintColor is implicitly changed when its superview or tintAdjustmentMode changes.
*/
- (void)tintColorDidChange NS_AVAILABLE_IOS(7_0);
@end
contentMode是UIView的属性,这个属性的值决定了,当视图的几何形状变化时如何复用它的内容。当视图第一次展示前,它会将自己的内容渲染成一张底层的bitmap。 然后视图的几何变化都不会使bitmap重新生成。而视图contentMode属性的值决定了bitmap是否缩放、位置在哪儿(固定在左边、右边、上面、下面、居中)。它是UIView的子类UIViewRendering的一个属性,它是以一个枚举值,默认是UIViewContentModeScaleToFill。
下面看一下这个枚举值。
typedef NS_ENUM(NSInteger, UIViewContentMode) {
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit, // contents scaled to fit with fixed aspect. remainder is transparent
UIViewContentModeScaleAspectFill, // contents scaled to fill with fixed aspect. some portion of content may be clipped.
UIViewContentModeRedraw, // redraw on bounds change (calls -setNeedsDisplay)
UIViewContentModeCenter, // contents remain same size. positioned adjusted.
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
};
三、几种填充模式讲解和效果展示
下面我们就说一下下面几种模式及其效果展示。
1. UIViewContentModeScaleToFill
这个是填充模式的默认选项。
UIViewContentModeScaleToFill
Scales the content to fit the size of itself by changing the aspect ratio of the content if necessary.
改变内容的高宽比例,缩放内容,UIView中完整显示内容,填满UIView,
上面那个模拟器图就是这个内容模式下的效果,imageView的尺寸没有改变,这里改变的是所给图像的宽高比例,并且全部填充到UIImageView里面。下面看代码和效果。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeScaleToFill;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
效果展示如下所示。
2. UIViewContentModeScaleAspectFit
UIViewContentModeScaleAspectFit
Scales the content to fit the size of the view by maintaining the aspect ratio. Any remaining area of the view’s bounds is transparent.
保持内容的高宽比,缩放内容,完整显示内容,最大化填充UIview,没填充上的区域透明
下面我们看一下代码和效果。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeScaleAspectFit;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
3. UIViewContentModeScaleAspectFill
UIViewContentModeScaleAspectFill
Scales the content to fill the size of the view. Some portion of the content may be clipped to fill the view’s bounds.
保持内容高宽比,缩放内容,超出视图的部分内容会被裁减,填充UIView
下面看一下效果图和代码。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeScaleAspectFill;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
这个方法严格按照图像的宽高比进行填充,超过的部分会被裁剪掉,好处就是这个方法保证不会失真,可以看见右边的纸盒子已经被裁剪掉了。
4. UIViewContentModeRedraw
UIViewContentModeRedraw
The option to redisplay the view when the bounds change by invoking the setNeedsDisplay method.
当View的bounds改变,系统会调用setNeedsDisplay,重新绘制视图
下面看代码和效果。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeRedraw;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
coverImageView.frame = CGRectMake(50.0, 300.0, 220, 130.0);
});
}
我这里采用了延时处理,3s后更改imageview的bounds,系统会自动调用setNeedsDisplay方法,重新绘制,效果如下图所示。
5. UIViewContentModeCenter
UIViewContentModeCenter
The option to center the content in the view’s bounds, keeping the proportions the same.
不缩放,内容在视图中间
下面我们就看一下代码和效果。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeCenter;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
下面看效果图。
6. UIViewContentModeTop
UIViewContentModeTop
不缩放,内容在视图上部。
下面看一下代码和效果。
- (void)setupUI
{
UIImageView *coverImageView = [[UIImageView alloc] init];
coverImageView.contentMode = UIViewContentModeTop;
coverImageView.clipsToBounds = YES;
coverImageView.layer.borderColor = [UIColor blueColor].CGColor;
coverImageView.layer.borderWidth = 0.5;
coverImageView.image = [UIImage imageNamed:@"girl_or_women"];
coverImageView.frame = CGRectMake(50.0, 300.0, 200, 200.0);
[self.view addSubview:coverImageView];
self.coverImageView = coverImageView;
}
剩下的其他几种方法很不常用而且简单易懂这里就不说了。借用网络上的一张图片让大家看看,所有填充模式的填充效果如下。
这里大家可能会有个需求,如果我需要进行一个图像固定区域的裁剪,该如何进行呢,下面给大家一个方法,如下所示。
+ (UIImage *)clipWithClipImage:(UIImage *)clipImage;
{
float width = clipImage.size.width > clipImage.size.height ? clipImage.size.height : clipImage.size.width;
CGSize clipSize = CGSizeMake(width, width);
UIGraphicsBeginImageContext(clipSize);
[clipImage drawInRect:CGRectMake(0,0,width,width)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
上面这个方法可以在任意点开始截取,并且截取任意size大小的图片。
后记
未完,待续~~~~