在@IB_Designable 上的新位置重绘 UIImage
Making UIImage redraw on new position on @IB_Designable
我正在创建这个 IB_Designable class。它就像一个滑块。见图片。这两个元素都是用很少可拉伸的 UIImages
.
创建的
我有这个 66x66 pt 的红色方块。这个正方形必须在灰色矩形内的 X 方向上滑动。
我已经创建了这个 class:
HEADER
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface MyClass : UIView
// minimum and maximum slider value
@property (assign, nonatomic) IBInspectable CGFloat minimumValue;
@property (assign, nonatomic) IBInspectable CGFloat maximumValue;
@property (assign, nonatomic) IBInspectable CGFloat value;
@end
实施
#import "MyClass.h"
@interface MyClass() {
__weak IBOutlet UIView *topContainer;
CGFloat minimumThumbCoordinate;
CGFloat maximumThumbCoordinate;
}
@property (weak, nonatomic) IBOutlet UIImageView *thumb;
@end
@implementation MyClass
- (void)awakeFromNib {
[super awakeFromNib];
// topContainer contains everything
CGRect topContainerBounds = [topContainer bounds];
CGRect thumbBounds = [self.thumb bounds];
CGFloat topContainerWidth = CGRectGetWidth(topContainerBounds);
CGFloat thumbWidth = CGRectGetWidth(thumbBounds);
minimumThumbCoordinate = floorf(thumbWidth / 2.0f);
maximumThumbCoordinate = floorf(topContainerWidth - minimumThumbCoordinate);
}
-(void)setValue:(CGFloat)value {
if ((value < self.minimumValue) || (value > self.maximumValue)) return;
// normalize values
CGFloat minimumValueNormalized = self.minimumValue;
CGFloat maximumValueNormalized = self.maximumValue;
CGFloat desiredValue = value;
if ((minimumValueNormalized < 0) && (maximumValueNormalized > 0)) {
CGFloat absoluteMinimum = fabsf(self.minimumValue);
minimumValueNormalized = 0;
maximumValueNormalized += absoluteMinimum;
desiredValue += absoluteMinimum;
}
CGFloat percentage = desiredValue/maximumValueNormalized;
// find coordinate
CGFloat coordinateRange = maximumThumbCoordinate - minimumThumbCoordinate;
CGFloat relativeCoordinate = percentage * coordinateRange;
CGPoint center = CGPointMake(relativeCoordinate, self.thumb.center.y);
[self.thumb setCenter: center];
}
问题是当我在界面生成器上设置值时,setValue
方法不会使拇指移动...有什么想法吗?
您不能在故事板中添加缩略图的组件。在这种情况下,我怀疑您的 thumb
图像视图出口在 IB 中预览时是 nil
,因此更改 value
它可能不会更新任何子视图。
您通常通过以编程方式添加子视图来解决这个问题,例如类似于:
// MyClass.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface MyClass : UIView
@property (nonatomic, strong) IBInspectable UIImage *thumbnailImage;
@property (nonatomic) IBInspectable CGFloat minimumValue;
@property (nonatomic) IBInspectable CGFloat maximumValue;
@property (nonatomic) IBInspectable CGFloat value;
@property (nonatomic, readonly) CGFloat percent;
@end
和
// MyClass.m
#import "MyClass.h"
@interface MyClass ()
@property (weak, nonatomic) UIImageView *thumbnailView;
@property (nonatomic) CGFloat thumbWidth;
@end
@implementation MyClass
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self configureView];
}
return self;
}
- (void)configureView {
UIImageView *thumb = [[UIImageView alloc] initWithImage:self.thumbnailImage];
[self addSubview:thumb];
thumb.backgroundColor = [UIColor lightGrayColor]; // in case no image has been set
thumb.clipsToBounds = true; // in case you change your `contentMode`
thumb.contentMode = UIViewContentModeScaleToFill;
_thumbnailView = thumb;
_thumbWidth = 44; // maybe you have another IBInspectable property for this...
[self layoutThumbnail];
}
- (void)setThumbnailImage:(UIImage *)image {
self.thumbnailView.image = image;
}
- (CGFloat)percent {
CGFloat range = self.maximumValue - self.minimumValue;
return range ? (self.value - self.minimumValue) / range : 0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutThumbnail];
}
- (void)layoutThumbnail {
CGFloat minX = self.thumbWidth / 2;
CGFloat maxX = self.bounds.size.width - self.thumbWidth / 2;
CGRect frame = CGRectMake(self.percent * (maxX - minX) + minX - self.thumbWidth / 2, 0, self.thumbWidth, self.bounds.size.height);
self.thumbnailView.frame = frame;
}
- (void)setValue:(CGFloat)value {
if (value < self.minimumValue)
_value = self.minimumValue;
else if (value > self.maximumValue) {
_value = self.maximumValue;
} else {
_value = value;
}
[self layoutThumbnail];
}
@end
请注意,我不仅会在您更改值时更新缩略图框,还会在 layoutSubviews
时更新。如果您的控件在运行时更改大小(例如,用户旋转设备并且控件更改大小),您希望确保缩略图框架更新。我还制作了缩略图 属性,因此如果您愿意,您也可以在 IB 中进行设置。但也许您使用了一些 hardcoded/default 图像。我还认为我们用于布置缩略图的 percent
可能对使用该控件的代码有用,所以我公开了 属性.
现在,您正在使用图像视图,但如果您想要的只是主控件和缩略图的圆角,我不会使用图像,而只是圆角 layer
对于适当的 UIView
对象。另外,我不清楚你对某些事情的意图(例如,我不确定你的 topContainer
指的是什么......你通常不会引用可设计视图的视图层次结构之外的视图)。并且您将顶层视图引用为图像视图,如果您也想要那里的背景图像,您会这样做。
但这些细节与您的更广泛的问题无关。希望这能说明这个想法,如果您以编程方式创建子视图,您可以看到缩略图随着您更改 IBInspectable
value
.
而移动
我在这里看到了一些实现,其中人们想要定义带有插座的可设计视图的所有视图层次结构。但在那种情况下,您对所有这些子视图都有一个独立的 NIB。如果您在 Whosebug 中搜索 "IBDesignable NIB",您将看到指向相关答案的链接。
我正在创建这个 IB_Designable class。它就像一个滑块。见图片。这两个元素都是用很少可拉伸的 UIImages
.
我有这个 66x66 pt 的红色方块。这个正方形必须在灰色矩形内的 X 方向上滑动。
我已经创建了这个 class:
HEADER
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface MyClass : UIView
// minimum and maximum slider value
@property (assign, nonatomic) IBInspectable CGFloat minimumValue;
@property (assign, nonatomic) IBInspectable CGFloat maximumValue;
@property (assign, nonatomic) IBInspectable CGFloat value;
@end
实施
#import "MyClass.h"
@interface MyClass() {
__weak IBOutlet UIView *topContainer;
CGFloat minimumThumbCoordinate;
CGFloat maximumThumbCoordinate;
}
@property (weak, nonatomic) IBOutlet UIImageView *thumb;
@end
@implementation MyClass
- (void)awakeFromNib {
[super awakeFromNib];
// topContainer contains everything
CGRect topContainerBounds = [topContainer bounds];
CGRect thumbBounds = [self.thumb bounds];
CGFloat topContainerWidth = CGRectGetWidth(topContainerBounds);
CGFloat thumbWidth = CGRectGetWidth(thumbBounds);
minimumThumbCoordinate = floorf(thumbWidth / 2.0f);
maximumThumbCoordinate = floorf(topContainerWidth - minimumThumbCoordinate);
}
-(void)setValue:(CGFloat)value {
if ((value < self.minimumValue) || (value > self.maximumValue)) return;
// normalize values
CGFloat minimumValueNormalized = self.minimumValue;
CGFloat maximumValueNormalized = self.maximumValue;
CGFloat desiredValue = value;
if ((minimumValueNormalized < 0) && (maximumValueNormalized > 0)) {
CGFloat absoluteMinimum = fabsf(self.minimumValue);
minimumValueNormalized = 0;
maximumValueNormalized += absoluteMinimum;
desiredValue += absoluteMinimum;
}
CGFloat percentage = desiredValue/maximumValueNormalized;
// find coordinate
CGFloat coordinateRange = maximumThumbCoordinate - minimumThumbCoordinate;
CGFloat relativeCoordinate = percentage * coordinateRange;
CGPoint center = CGPointMake(relativeCoordinate, self.thumb.center.y);
[self.thumb setCenter: center];
}
问题是当我在界面生成器上设置值时,setValue
方法不会使拇指移动...有什么想法吗?
您不能在故事板中添加缩略图的组件。在这种情况下,我怀疑您的 thumb
图像视图出口在 IB 中预览时是 nil
,因此更改 value
它可能不会更新任何子视图。
您通常通过以编程方式添加子视图来解决这个问题,例如类似于:
// MyClass.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface MyClass : UIView
@property (nonatomic, strong) IBInspectable UIImage *thumbnailImage;
@property (nonatomic) IBInspectable CGFloat minimumValue;
@property (nonatomic) IBInspectable CGFloat maximumValue;
@property (nonatomic) IBInspectable CGFloat value;
@property (nonatomic, readonly) CGFloat percent;
@end
和
// MyClass.m
#import "MyClass.h"
@interface MyClass ()
@property (weak, nonatomic) UIImageView *thumbnailView;
@property (nonatomic) CGFloat thumbWidth;
@end
@implementation MyClass
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self configureView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self configureView];
}
return self;
}
- (void)configureView {
UIImageView *thumb = [[UIImageView alloc] initWithImage:self.thumbnailImage];
[self addSubview:thumb];
thumb.backgroundColor = [UIColor lightGrayColor]; // in case no image has been set
thumb.clipsToBounds = true; // in case you change your `contentMode`
thumb.contentMode = UIViewContentModeScaleToFill;
_thumbnailView = thumb;
_thumbWidth = 44; // maybe you have another IBInspectable property for this...
[self layoutThumbnail];
}
- (void)setThumbnailImage:(UIImage *)image {
self.thumbnailView.image = image;
}
- (CGFloat)percent {
CGFloat range = self.maximumValue - self.minimumValue;
return range ? (self.value - self.minimumValue) / range : 0;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutThumbnail];
}
- (void)layoutThumbnail {
CGFloat minX = self.thumbWidth / 2;
CGFloat maxX = self.bounds.size.width - self.thumbWidth / 2;
CGRect frame = CGRectMake(self.percent * (maxX - minX) + minX - self.thumbWidth / 2, 0, self.thumbWidth, self.bounds.size.height);
self.thumbnailView.frame = frame;
}
- (void)setValue:(CGFloat)value {
if (value < self.minimumValue)
_value = self.minimumValue;
else if (value > self.maximumValue) {
_value = self.maximumValue;
} else {
_value = value;
}
[self layoutThumbnail];
}
@end
请注意,我不仅会在您更改值时更新缩略图框,还会在 layoutSubviews
时更新。如果您的控件在运行时更改大小(例如,用户旋转设备并且控件更改大小),您希望确保缩略图框架更新。我还制作了缩略图 属性,因此如果您愿意,您也可以在 IB 中进行设置。但也许您使用了一些 hardcoded/default 图像。我还认为我们用于布置缩略图的 percent
可能对使用该控件的代码有用,所以我公开了 属性.
现在,您正在使用图像视图,但如果您想要的只是主控件和缩略图的圆角,我不会使用图像,而只是圆角 layer
对于适当的 UIView
对象。另外,我不清楚你对某些事情的意图(例如,我不确定你的 topContainer
指的是什么......你通常不会引用可设计视图的视图层次结构之外的视图)。并且您将顶层视图引用为图像视图,如果您也想要那里的背景图像,您会这样做。
但这些细节与您的更广泛的问题无关。希望这能说明这个想法,如果您以编程方式创建子视图,您可以看到缩略图随着您更改 IBInspectable
value
.
我在这里看到了一些实现,其中人们想要定义带有插座的可设计视图的所有视图层次结构。但在那种情况下,您对所有这些子视图都有一个独立的 NIB。如果您在 Whosebug 中搜索 "IBDesignable NIB",您将看到指向相关答案的链接。