Files
peko-ios/YuMi/Modules/YMMine/View/Medals/MedalsLevelIndicatorView.m
edwinQQQ 99db078b62 feat(勋章): 新增多等级高亮功能并优化勋章展示逻辑
refactor(勋章排行): 重构排行榜分页加载和刷新逻辑

fix(会话): 优化官方账号判断逻辑和跳转处理

style(UI): 调整勋章排行榜和游戏菜单UI布局

chore: 更新Podfile配置和bitcode框架列表
2025-07-03 19:35:17 +08:00

752 lines
24 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// MedalsLevelIndicatorView.m
// YuMi
//
// Created by P on 2025/6/18.
//
#import "MedalsLevelIndicatorView.h"
#import "UIImage+Utils.h"
#import "MedalsModel.h"
#import <QGVAPWrapView.h>
#import "XPRoomGiftAnimationParser.h"
// 等级指示器视图
@interface LevelItemView : UIView
@property (nonatomic, strong) UILabel *levelLabel;
@property (nonatomic, strong) UIView *dotView; // 圆点视图
@property (nonatomic, strong) NetImageView *imageView; // 图片视图
@property (nonatomic, strong) VAPView *mp4View; // MP4 视图
@property (nonatomic, strong) XPRoomGiftAnimationParser *mp4Parser; // MP4 解析器
@property (nonatomic, assign) BOOL isSelected;
@property (nonatomic, assign) NSInteger level;
@property (nonatomic, assign) BOOL hasImage;
@property (nonatomic, assign) BOOL hasMP4;
@property (nonatomic, strong) UIImage *originalImage; // 保存原始彩色图片
@property (nonatomic, strong) UIImage *grayImage; // 保存灰度图片
@property (nonatomic, copy) NSString *cachedImageUrl; // 缓存图片URL用于延迟加载
@property (nonatomic, copy) NSString *cachedMP4Url; // 缓存MP4 URL用于延迟加载
- (instancetype)initWithLevel:(NSInteger)level;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
- (void)setImageUrl:(NSString *)imageUrl;
- (void)setMP4Url:(NSString *)mp4Url;
- (void)createImageViewIfNeeded;
- (void)createMP4ViewIfNeeded;
- (void)stopMP4Playback;
@end
@implementation LevelItemView
- (instancetype)initWithLevel:(NSInteger)level {
self = [super init];
if (self) {
_level = level;
_isSelected = NO;
_hasImage = NO;
_hasMP4 = NO;
// 创建圆点视图
_dotView = [[UIView alloc] init];
_dotView.backgroundColor = UIColorFromRGB(0x8B54E8);
_dotView.layer.cornerRadius = 3; // 圆点半径
_dotView.clipsToBounds = YES;
[self addSubview:_dotView];
// 创建等级标签
self.levelLabel = [UILabel labelInitWithText:[NSString stringWithFormat:@"LV%ld", (long)level]
font:kFontMedium(10)
textColor:UIColorFromRGB(0x8B54E8)];
self.levelLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.levelLabel];
// 布局
[_dotView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(self);
make.width.height.mas_equalTo(6); // 圆点大小
}];
[self.levelLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(_dotView.mas_bottom).offset(4); // 圆点和文字的间距
make.leading.trailing.bottom.mas_equalTo(self);
}];
}
return self;
}
- (void)createImageViewIfNeeded {
if (_imageView) {
return;
}
// 创建图片视图
_imageView = [[NetImageView alloc] init];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
_imageView.clipsToBounds = YES;
[self addSubview:_imageView];
[_imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.bottom.mas_equalTo(_dotView.mas_top).offset(-4);
make.width.height.mas_equalTo(40);
}];
// 调整圆点位置
[_dotView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(_imageView.mas_bottom).offset(4);
make.width.height.mas_equalTo(6);
}];
// 如果有缓存的图片URL加载图片
if (_cachedImageUrl) {
[self loadImageWithUrl:_cachedImageUrl];
_cachedImageUrl = nil;
}
}
- (void)createMP4ViewIfNeeded {
if (_mp4View) {
return;
}
// 创建 MP4 视图
_mp4View = [[VAPView alloc] init];
_mp4View.contentMode = UIViewContentModeScaleAspectFill;
_mp4View.clipsToBounds = YES;
[self addSubview:_mp4View];
[_mp4View mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.bottom.mas_equalTo(_dotView.mas_top).offset(-4);
make.width.height.mas_equalTo(40);
}];
// 调整圆点位置
[_dotView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerX.mas_equalTo(self);
make.top.mas_equalTo(_mp4View.mas_bottom).offset(4);
make.width.height.mas_equalTo(6);
}];
// 如果有缓存的 MP4 URL加载 MP4
if (_cachedMP4Url) {
[self loadMP4WithUrl:_cachedMP4Url];
_cachedMP4Url = nil;
}
}
- (void)setImageUrl:(NSString *)imageUrl {
// 检查URL是否为空
#if 0
imageUrl = @"https://img.toto.im/mw600/9f0b0dd5ly1i2u07wq7g2j20n00w343s.jpg.webp";
#endif
if ([NSString isEmpty:imageUrl]) {
[self showDefaultPlaceholder];
return;
}
#if 0
// Debug 环境:使用原有逻辑
if ([imageUrl.lowercaseString hasSuffix:@"mp4"]) {
[self setMP4Url:imageUrl];
} else {
_hasImage = YES;
_hasMP4 = NO;
_cachedImageUrl = imageUrl;
// 停止 MP4 播放
[self stopMP4Playback];
// 如果已经创建了imageView直接加载图片
if (_imageView) {
[self loadImageWithUrl:imageUrl];
}
}
return;
#endif
// 新逻辑只显示图片MP4 显示占位图
if ([imageUrl.lowercaseString hasSuffix:@"mp4"]) {
// MP4 URL显示占位图保留MP4处理代码备用
[self showDefaultPlaceholder];
// [self setMP4Url:imageUrl]; // 保留备用
} else if ([NSString isValidImageURL:imageUrl]) {
// 验证为有效图片URL
_hasImage = YES;
_hasMP4 = NO;
_cachedImageUrl = imageUrl;
// 停止 MP4 播放
[self stopMP4Playback];
// 如果已经创建了imageView直接加载图片
if (_imageView) {
[self loadImageWithUrl:imageUrl];
}
} else {
// URL 不是有效的图片,显示默认占位图
[self showDefaultPlaceholder];
}
}
/// 显示默认占位图
- (void)showDefaultPlaceholder {
// 停止 MP4 播放
[self stopMP4Playback];
_hasImage = NO;
_hasMP4 = NO;
_cachedImageUrl = nil;
_cachedMP4Url = nil;
if (_imageView) {
_imageView.hidden = NO;
_imageView.image = [UIImageConstant defaultEmptyPlaceholder];
}
if (_mp4View) {
_mp4View.hidden = YES;
}
}
- (void)setMP4Url:(NSString *)mp4Url {
_hasMP4 = YES;
_hasImage = NO;
_cachedMP4Url = mp4Url;
// 隐藏图片视图
if (_imageView) {
_imageView.hidden = YES;
}
// 如果已经创建了mp4View直接加载MP4
if (_mp4View) {
[self loadMP4WithUrl:mp4Url];
}
}
- (void)loadImageWithUrl:(NSString *)imageUrl {
// 加载图片并处理灰度效果
@kWeakify(self);
[_imageView loadImageWithUrl:imageUrl completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) {
@kStrongify(self);
if (image) {
UIImage *cutImage = [image cutImage:CGSizeMake(40, 40)];
self.hasImage = YES;
self.originalImage = cutImage;
self.grayImage = [cutImage grayscaleImage];
[self updateImageEffect];
}
}];
}
- (void)loadMP4WithUrl:(NSString *)mp4Url {
if (!_mp4Parser) {
_mp4Parser = [[XPRoomGiftAnimationParser alloc] init];
}
@kWeakify(self);
[_mp4Parser parseWithURL:mp4Url
completionBlock:^(NSString * _Nullable videoUrl) {
@kStrongify(self);
if (![NSString isEmpty:videoUrl]) {
[self.mp4View setMute:YES];
[self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:nil];
// 显示 MP4 视图,隐藏图片视图
self.mp4View.hidden = NO;
if (self.imageView) {
self.imageView.hidden = YES;
}
// MP4 加载完成后,应用当前的选中状态效果
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self updateImageEffect];
});
}
} failureBlock:^(NSError * _Nullable error) {
@kStrongify(self);
// MP4 加载失败时的处理
NSLog(@"MP4 加载失败: %@", error);
[self showDefaultPlaceholder];
}];
}
- (void)stopMP4Playback {
if (_mp4View) {
[_mp4View stopHWDMP4];
_mp4View.hidden = YES;
}
}
// 更新图片效果
- (void)updateImageEffect {
if (_hasImage && _imageView) {
if (_isSelected && _originalImage) {
// 选中状态:显示彩色图片
self.imageView.image = _originalImage;
} else if (!_isSelected && _grayImage) {
// 非选中状态:显示灰度图片
self.imageView.image = _grayImage;
}
}
// MP4 的选中效果处理 - 使用简单的 alpha 透明度
// if (_hasMP4 && _mp4View) {
// if (_isSelected) {
// _mp4View.alpha = 1.0; // 选中状态:完全不透明
// } else {
// _mp4View.alpha = 0.3; // 非选中状态:半透明
// }
// }
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
if (_isSelected == selected) {
return;
}
_isSelected = selected;
void (^updateBlock)(void) = ^{
UIColor *color = selected ? [UIColor whiteColor] : UIColorFromRGB(0x8B54E8);
self.levelLabel.textColor = color;
self.dotView.backgroundColor = color;
// 如果有图片或MP4更新效果
if (self.hasImage || self.hasMP4) {
[self updateImageEffect];
}
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
@end
// 等级指示器容器视图
@interface MedalsLevelIndicatorView()
@property (nonatomic, strong) NSMutableArray<LevelItemView *> *levelItems;
@property (nonatomic, strong) NSMutableArray<UIView *> *connectionLines; // 连接线数组
@property (nonatomic, assign) NSInteger maxLevel;
@property (nonatomic, assign) NSInteger selectedLevel;
@end
@implementation MedalsLevelIndicatorView
- (instancetype)init {
self = [super init];
if (self) {
_levelItems = [NSMutableArray array];
_connectionLines = [NSMutableArray array];
_selectedLevel = 1;
_indicatorType = MedalsLevelIndicatorTypeNormal;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self addGestureRecognizer:tapGesture];
}
return self;
}
- (void)setupWithMaxLevel:(NSInteger)maxLevel {
if (maxLevel < 0) {
maxLevel = 1;
}
if (_maxLevel == maxLevel && _levelItems.count == maxLevel) {
return;
}
_maxLevel = maxLevel;
// 清除现有的等级指示器和连接线
for (UIView *view in _levelItems) {
[view removeFromSuperview];
}
[_levelItems removeAllObjects];
for (UIView *line in _connectionLines) {
[line removeFromSuperview];
}
[_connectionLines removeAllObjects];
// 创建新的等级指示器
CGFloat itemWidth = 25.0; // 每个等级指示器的宽度
CGFloat leftMargin = 20.0; // 左边距
CGFloat rightMargin = 20.0; // 右边距
// 计算可用宽度(考虑左右边距)
CGFloat availableWidth = self.bounds.size.width - leftMargin - rightMargin;
// 计算间距和起始位置
CGFloat spacing = 0;
CGFloat startX = leftMargin;
if (maxLevel == 1) {
// 只有一个元素时,居中显示
startX = (self.bounds.size.width - itemWidth) / 2.0;
} else if (maxLevel > 1) {
// 多个元素时,计算间距
// 总的元素宽度
CGFloat totalItemWidth = itemWidth * maxLevel;
// 可用于间距的宽度
CGFloat totalSpacingWidth = availableWidth - totalItemWidth;
// 间距数量 = 元素数量 - 1
spacing = totalSpacingWidth / (maxLevel - 1);
// 如果间距太小,设置一个最小值,并重新调整布局
if (spacing < 10.0) {
spacing = 10.0;
// 重新计算总宽度
CGFloat totalWidth = totalItemWidth + spacing * (maxLevel - 1);
// 重新计算起始位置,使整体居中
startX = (self.bounds.size.width - totalWidth) / 2.0;
}
}
for (NSInteger i = 1; i <= maxLevel; i++) {
// 创建等级指示器
LevelItemView *levelItem = [[LevelItemView alloc] initWithLevel:i];
[self addSubview:levelItem];
[_levelItems addObject:levelItem];
// 设置位置
CGFloat x = startX + (i - 1) * (itemWidth + spacing);
[levelItem mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(itemWidth);
make.height.mas_equalTo(self);
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(self).offset(x);
}];
// 如果不是第一个,添加连接线
if (i > 1) {
UIView *line = [[UIView alloc] init];
line.backgroundColor = UIColorFromRGB(0x8B54E8); // 默认非选中颜色
[self insertSubview:line atIndex:0];
[_connectionLines addObject:line];
// 连接线位置:从上一个圆点到当前圆点
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(1); // 线的高度
make.centerY.mas_equalTo(levelItem.dotView);
make.leading.mas_equalTo(_levelItems[i-2].dotView.mas_centerX);
make.trailing.mas_equalTo(levelItem.dotView.mas_centerX);
}];
}
}
// 默认选中LV1
[self setSelectedLevel:1 animated:NO];
// 根据类型设置图片可见性
[self updateImageVisibility];
}
- (void)layoutSubviews {
[super layoutSubviews];
if (_maxLevel > 0 && _levelItems.count > 0) {
// 创建新的等级指示器
CGFloat itemWidth = 25.0; // 每个等级指示器的宽度
CGFloat leftMargin = 20.0; // 左边距
CGFloat rightMargin = 20.0; // 右边距
if (_levelItems.count > 4) {
itemWidth = 16;
leftMargin = 0;
rightMargin = 10;
}
// 计算可用宽度(考虑左右边距)
CGFloat availableWidth = self.bounds.size.width - leftMargin - rightMargin;
// 计算间距和起始位置
CGFloat spacing = 0;
CGFloat startX = leftMargin;
if (_maxLevel == 1) {
// 只有一个元素时,居中显示
startX = (self.bounds.size.width - itemWidth) / 2.0;
} else if (_maxLevel > 1) {
// 多个元素时,计算间距
// 总的元素宽度
CGFloat totalItemWidth = itemWidth * _maxLevel;
// 可用于间距的宽度
CGFloat totalSpacingWidth = availableWidth - totalItemWidth;
// 间距数量 = 元素数量 - 1
spacing = totalSpacingWidth / (_maxLevel - 1);
// 如果间距太小,设置一个最小值,并重新调整布局
if (spacing < 10.0) {
spacing = 10.0;
// 重新计算总宽度
CGFloat totalWidth = totalItemWidth + spacing * (_maxLevel - 1);
// 重新计算起始位置,使整体居中
startX = (self.bounds.size.width - totalWidth) / 2.0;
if (_levelItems.count > 4) {
startX -= spacing/2;
}
}
}
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
CGFloat x = startX + i * (itemWidth + spacing);
[item mas_updateConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self).offset(x);
}];
}
// 更新连接线
for (NSInteger i = 0; i < _connectionLines.count && i + 1 < _levelItems.count; i++) {
UIView *line = _connectionLines[i];
[line mas_updateConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(_levelItems[i].dotView.mas_centerX);
make.trailing.mas_equalTo(_levelItems[i+1].dotView.mas_centerX);
}];
}
}
}
- (void)setSelectedLevel:(NSInteger)level animated:(BOOL)animated {
if (level < 1 || level > _maxLevel) {
return;
}
_selectedLevel = level;
// 更新等级指示器状态
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
[item setSelected:(i + 1) <= level animated:animated];
}
// 更新连接线状态
for (NSInteger i = 0; i < _connectionLines.count; i++) {
UIView *line = _connectionLines[i];
// 连接线的状态取决于它连接的两个等级指示器是否都被选中
// 如果连接线两端的等级指示器都被选中,则连接线也被选中
BOOL isSelected = (i + 2) <= level;
void (^updateBlock)(void) = ^{
line.backgroundColor = isSelected ? [UIColor whiteColor] : UIColorFromRGB(0x8B54E8);
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
}
- (void)resetToLevel:(NSInteger)level {
// 停止所有MP4播放
for (LevelItemView *item in _levelItems) {
[item stopMP4Playback];
}
// 重置选中状态
if (level <= 0) {
// 重置到初始状态
[self setSelectedLevel:1 animated:NO];
} else {
// 重置到指定等级
[self setSelectedLevel:level animated:NO];
}
}
- (void)setIndicatorType:(MedalsLevelIndicatorType)indicatorType {
_indicatorType = indicatorType;
// 如果已经创建了视图,需要根据类型进行调整
if (_levelItems.count > 0) {
// 如果是带图片类型,创建相应的视图
if (indicatorType == MedalsLevelIndicatorTypeWithImage) {
for (LevelItemView *item in _levelItems) {
if (item.hasMP4) {
[item createMP4ViewIfNeeded];
} else if (item.hasImage) {
[item createImageViewIfNeeded];
}
}
}
[self updateImageVisibility];
}
}
// 根据指示器类型更新图片可见性
- (void)updateImageVisibility {
for (LevelItemView *item in _levelItems) {
if (item.imageView) {
item.imageView.hidden = (_indicatorType == MedalsLevelIndicatorTypeNormal);
}
if (item.mp4View) {
item.mp4View.hidden = (_indicatorType == MedalsLevelIndicatorTypeNormal);
}
}
}
- (void)setImageUrl:(NSString *)imageUrl forLevel:(NSInteger)level {
if (level < 1 || level > _maxLevel || level > _levelItems.count) {
return;
}
LevelItemView *item = _levelItems[level - 1];
// 根据指示器类型决定是否创建和加载图片/MP4
if (_indicatorType == MedalsLevelIndicatorTypeWithImage) {
// 判断是 MP4 还是图片
if ([imageUrl.lowercaseString hasSuffix:@"mp4"]) {
[item createMP4ViewIfNeeded]; // 确保创建了 mp4View
} else {
[item createImageViewIfNeeded]; // 确保创建了 imageView
}
[item setImageUrl:imageUrl];
} else {
// 在普通模式下只保存URL不创建视图
[item setImageUrl:imageUrl];
}
}
- (void)setSeriesItems:(NSArray <MedalVo *> *)seriesItems {
_seriesItems = seriesItems;
// 如果没有系列数据或者等级指示器还没有创建,直接返回
if (!seriesItems || seriesItems.count == 0 || _levelItems.count == 0) {
return;
}
for (MedalVo *medalVo in seriesItems) {
if (medalVo && medalVo.picUrl) {
[self setImageUrl:medalVo.picUrl forLevel:medalVo.level];
}
}
}
- (void)handleTap:(UITapGestureRecognizer *)gesture {
CGPoint location = [gesture locationInView:self];
for (NSInteger i = 0; i < _levelItems.count; i++) {
LevelItemView *item = _levelItems[i];
CGPoint itemLocation = [self convertPoint:location toView:item];
// 检查点击是否在 item 的边界内
if (CGRectContainsPoint(item.bounds, itemLocation)) {
NSInteger level = i + 1;
[self setSelectedLevel:level animated:YES];
if (_levelSelectedBlock) {
_levelSelectedBlock(level);
}
break;
}
}
}
- (void)setHighlightLevels:(NSArray<NSNumber *> *)levels animated:(BOOL)animated {
if (!levels || levels.count == 0 || _levelItems.count == 0) {
return;
}
// 先将所有 item 重置为非高亮状态
for (LevelItemView *item in _levelItems) {
[item setSelected:NO animated:animated];
}
// 将所有连接线重置为非高亮状态
for (UIView *line in _connectionLines) {
void (^updateBlock)(void) = ^{
line.backgroundColor = UIColorFromRGB(0x8B54E8); // 非选中颜色
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
// 高亮指定的等级
NSMutableSet *highlightedLevels = [NSMutableSet set];
for (NSNumber *levelNumber in levels) {
NSInteger level = [levelNumber integerValue];
if (level >= 1 && level <= _maxLevel) {
[highlightedLevels addObject:@(level)];
// 设置对应的 item 为高亮状态
LevelItemView *item = _levelItems[level - 1];
[item setSelected:YES animated:animated];
}
}
// 更新连接线状态
// 只有当连接线两端的等级都被高亮时,连接线才高亮
for (NSInteger i = 0; i < _connectionLines.count; i++) {
UIView *line = _connectionLines[i];
NSInteger startLevel = i + 1;
NSInteger endLevel = i + 2;
BOOL isSelected = [highlightedLevels containsObject:@(startLevel)] &&
[highlightedLevels containsObject:@(endLevel)];
void (^updateBlock)(void) = ^{
line.backgroundColor = isSelected ? [UIColor whiteColor] : UIColorFromRGB(0x8B54E8);
};
if (animated) {
[UIView animateWithDuration:0.2 animations:updateBlock];
} else {
updateBlock();
}
}
}
// 计算布局参数
- (void)calculateLayoutParams:(CGFloat *)itemWidth spacing:(CGFloat *)spacing startX:(CGFloat *)startX forMaxLevel:(NSInteger)maxLevel {
*itemWidth = 25.0; // 每个等级指示器的宽度
CGFloat leftMargin = 20.0; // 左边距
CGFloat rightMargin = 20.0; // 右边距
// 计算可用宽度(考虑左右边距)
CGFloat availableWidth = self.bounds.size.width - leftMargin - rightMargin;
// 计算间距和起始位置
*spacing = 0;
*startX = leftMargin;
if (maxLevel == 1) {
// 只有一个元素时,居中显示
*startX = (self.bounds.size.width - (*itemWidth)) / 2.0;
} else if (maxLevel > 1) {
// 多个元素时,计算间距
// 总的元素宽度
CGFloat totalItemWidth = (*itemWidth) * maxLevel;
// 可用于间距的宽度
CGFloat totalSpacingWidth = availableWidth - totalItemWidth;
// 间距数量 = 元素数量 - 1
*spacing = totalSpacingWidth / (maxLevel - 1);
// 如果间距太小,设置一个最小值,并重新调整布局
if (*spacing < 10.0) {
*spacing = 10.0;
// 重新计算总宽度
CGFloat totalWidth = totalItemWidth + (*spacing) * (maxLevel - 1);
// 重新计算起始位置,使整体居中
*startX = (self.bounds.size.width - totalWidth) / 2.0;
}
}
}
@end