当前位置: 首页> 科技> 互联网 > 软件设计学什么_苏州网站建设系统价格合理_网页模板源代码_网站策划是什么

软件设计学什么_苏州网站建设系统价格合理_网页模板源代码_网站策划是什么

时间:2025/7/9 13:57:54来源:https://blog.csdn.net/callauxiliary/article/details/146059119 浏览次数:0次
软件设计学什么_苏州网站建设系统价格合理_网页模板源代码_网站策划是什么

优雅实现水平滚动工具栏:UIScrollView与UIStackView的完美组合

本文将介绍如何通过UIScrollView与UIStackView的黄金组合,实现一个灵活高效的水平滚动工具栏。这种组合方案具有以下优势:

  1. 自动布局:UIScrollView提供滚动容器,UIStackView管理动态布局
  2. 命令模式:采用协议化设计实现按钮与业务逻辑解耦
  3. 动态扩展:支持按钮的灵活增减
  4. 性能优化:原生控件组合带来流畅滚动体验
  5. 代码简洁:告别复杂的布局计算

核心实现原理

1. 视图层级结构
ButtonBarView
└── UIScrollView└── UIStackView (Horizontal)└── 自定义按钮视图(SLVerticalButtonView)×N
2. 关键布局策略
// 核心约束设置
// StackView 约束到 ScrollView 的 ContentLayoutGuide
[_stackView.topAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.topAnchor],
[_stackView.leadingAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.leadingAnchor],
[_stackView.trailingAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.trailingAnchor],
[_stackView.bottomAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.bottomAnchor],// StackView 高度与 ScrollView 的 FrameLayoutGuide 一致
[_stackView.heightAnchor constraintEqualToAnchor:_scrollView.frameLayoutGuide.heightAnchor]
3. 动态内容更新
- (void)setCommands:(NSArray<id<Command>> *)commands {// 清空旧视图// 动态生成新按钮// 更新布局
}
// 更新布局
- (void)updateStackViewWidthIfNeeded {[self.scrollView layoutIfNeeded]; // 立即布局以获取准确宽度CGFloat contentWidth = self.stackView.frame.size.width;CGFloat scrollViewWidth = self.scrollView.bounds.size.width;if (contentWidth < scrollViewWidth && self.commands.count > 0) {if (!self.stackViewWidthConstraint) {// 添加约束使stackView宽度等于scrollView的宽度self.stackViewWidthConstraint = [self.stackView.widthAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.widthAnchor];self.stackViewWidthConstraint.priority = UILayoutPriorityRequired;[self.stackViewWidthConstraint setActive:YES];[self.scrollView layoutIfNeeded]; // 应用新约束}} else {if (self.stackViewWidthConstraint) {[self.stackViewWidthConstraint setActive:NO];self.stackViewWidthConstraint = nil;[self.scrollView layoutIfNeeded];}}
}

完整实现代码(Objective-C)

//====================
// 命令协议
//====================
@protocol Command <NSObject>
@property (strong, nonatomic) ExtendCompositional *descriptor;
- (void)execute;
@end//====================
// 命令工厂协议
//====================
@protocol CommandFactory <NSObject>
- (NSArray<id<Command>> *)commandsForContext:(UIViewController *)context;
@end//====================
// 基础命令抽象类
//====================
@interface Command : NSObject <Command>
@property (weak, nonatomic) UIViewController *context;
+ (instancetype)commandWithContext:(UIViewController *)context;
@end@implementation Command
@synthesize descriptor = _descriptor;
+ (instancetype)commandWithContext:(UIViewController *)context {Command *cmd = [self new];cmd.context = context;return cmd;
}- (void)execute { /* 子类实现 */ }
@end//====================
// 工具栏视图
//====================
@interface ButtonBarView : UIView<UIScrollViewDelegate>
@property (strong, nonatomic) UIScrollView *scrollView;
@property (strong, nonatomic) UIStackView *stackView;
@property (strong, nonatomic) NSArray<id<Command>> *commands;
@end@interface ButtonBarView ()
@property (nonatomic, strong) NSLayoutConstraint *stackViewWidthConstraint;
@end@implementation ButtonBarView
- (instancetype)initWithFrame:(CGRect)frame
{self = [super initWithFrame:frame];if (self) {[self setupScrollSystem];}return self;
}- (void)setupScrollSystem {_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];_scrollView.showsHorizontalScrollIndicator = NO;_scrollView.delegate = self;_stackView = [[UIStackView alloc] init];_stackView.axis = UILayoutConstraintAxisHorizontal;_stackView.alignment = UIStackViewAlignmentFill;_stackView.distribution = UIStackViewDistributionFillEqually;_stackView.spacing = 20;[_scrollView addSubview:_stackView];[self addSubview:_scrollView];// 布局约束_stackView.translatesAutoresizingMaskIntoConstraints = NO;_scrollView.translatesAutoresizingMaskIntoConstraints = NO;[NSLayoutConstraint activateConstraints:@[[_scrollView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],[_scrollView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],[_scrollView.topAnchor constraintEqualToAnchor:self.topAnchor],[_scrollView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],// StackView 约束到 ScrollView 的 ContentLayoutGuide[_stackView.topAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.topAnchor],[_stackView.leadingAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.leadingAnchor],[_stackView.trailingAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.trailingAnchor],[_stackView.bottomAnchor constraintEqualToAnchor:_scrollView.contentLayoutGuide.bottomAnchor],// StackView 高度与 ScrollView 的 FrameLayoutGuide 一致[_stackView.heightAnchor constraintEqualToAnchor:_scrollView.frameLayoutGuide.heightAnchor]]];
}
- (void)setCommands:(NSArray<id<Command>> *)commands {_commands = commands;// 清空旧按钮[_stackView.arrangedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)];// 动态生成按钮[commands enumerateObjectsUsingBlock:^(id<Command> command, NSUInteger idx, BOOL *stop) {SLVerticalButtonView *button = [[SLVerticalButtonView alloc] initWithFrame:CGRectZero target:command action:@selector(execute)];button.image = command.descriptor.image;button.title = command.descriptor.title;button.identifier = command.descriptor.identifier;[button setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];[self.stackView addArrangedSubview:button];}];[self updateStackViewWidthIfNeeded];
}
- (void)layoutSubviews {[super layoutSubviews];[self updateStackViewWidthIfNeeded];
}
- (void)updateStackViewWidthIfNeeded {[self.scrollView layoutIfNeeded]; // 立即布局以获取准确宽度CGFloat contentWidth = self.stackView.frame.size.width;CGFloat scrollViewWidth = self.scrollView.bounds.size.width;if (contentWidth < scrollViewWidth && self.commands.count > 0) {if (!self.stackViewWidthConstraint) {// 添加约束使stackView宽度等于scrollView的宽度self.stackViewWidthConstraint = [self.stackView.widthAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.widthAnchor];self.stackViewWidthConstraint.priority = UILayoutPriorityRequired;[self.stackViewWidthConstraint setActive:YES];[self.scrollView layoutIfNeeded]; // 应用新约束}} else {if (self.stackViewWidthConstraint) {[self.stackViewWidthConstraint setActive:NO];self.stackViewWidthConstraint = nil;[self.scrollView layoutIfNeeded];}}
}
@end

使用示例

1. 创建命令工厂
@interface CommandFactory : NSObject <CommandFactory>
@end
@implementation CommandFactory
- (NSArray<id<Command>> *)commandsForContext:(UIViewController *)context {return @[[AddPhotoCommand commandWithContext:context],[AddTextCommand commandWithContext:context],[AddBackgroundCommand commandWithContext:context],[AddFilterCommand commandWithContext:context]];
}@end
2. 在ViewController中使用
- (void)viewDidLoad {[super viewDidLoad];// 创建工具栏ButtonBarView *toolbar = [[ButtonBarView alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width, 80)];// 获取命令列表id<CommandFactory> factory = [CommandFactory new];toolbar.commands = [factory commandsForContext:self];[self.view addSubview:toolbar];
}
3. 自定义按钮示例(SLVerticalButtonView)
// 实现垂直排列的图标+文字按钮
@implementation SLVerticalButtonView- (instancetype)initWithFrame:(CGRect)frame target:(id)target action:(SEL)action {self = [super initWithFrame:frame];// 添加图片视图和文字标签// 设置点击事件[self addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];return self;
}
@end

关键点解析

  1. 布局锚点选择

    • contentLayoutGuide:决定可滚动内容范围
    • frameLayoutGuide:限制可视区域范围
  2. 性能优化

    • 复用UIStackView的自动布局
    • 避免复杂图层结构
  3. 扩展性设计

    • 遵循命令模式
    • 支持动态命令配置

效果展示

水平滚动工具栏将自动根据内容多少:

  • 内容少时:居中显示
  • 内容多时:支持左右滑动
  • 自动维持统一高度

这种实现方案完美结合了UIScrollView的滚动能力和UIStackView的自动布局优势,特别适合需要动态生成水平排列元素的场景,避免选择UICollectionView层级结构比较多的视图。通过命令模式的引入,使得每个按钮的行为可以灵活配置,极大提高了代码的可维护性和扩展性。

关键字:软件设计学什么_苏州网站建设系统价格合理_网页模板源代码_网站策划是什么

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: