新手自定义UISlider,可做Slider,也可做ProgressBar

本文介绍了一种自定义UISlider的方法,通过创建一个继承自UIView的类来实现完全自定义的滑块组件。文章提供了关键代码示例,并展示了如何处理触摸事件以更新滑块的位置。

UI设计给出了设计图,界面却是不错,看着霸气十足,可是里面的控件,都需要自定义,没有一个不需要折腾的,也罢,就一直在折腾了。折腾中遇到一个问题,UISlder的完全自定义,完全摒弃原生态的东西,开始有点无厘头,慢慢页折腾出来了,今天给出一些关键代码和demo,只是简单的实现,很多功能有待发掘。

首先看一下UI给出的效果图:



网上有一种自定义UISlider的方法,但是最后放弃了,因为依然没有达到所需,也是比人才疏学浅。

下面就是我自定义的类UISlider的部分代码:

1、首先我声明了一个继承子UIView的类,因为我觉的使用UIView页绘制更加快捷。

//
//  Slider.h
//  SliderPro
//
//  Created by Yongchao Zhang on 12-9-13.
//  Copyright (c) 2012年 Yongchao Zhang. All rights reserved.
//

#import <UIKit/UIKit.h>

@protocol SliderDelegate;

@interface Slider : UIView{
 
    //放置时间
    UIImageView *popView;
    //箭头
    UIImageView *tagView;
    
    UILabel *currentLabel;
    UILabel *totalLabel;
    NSString *currentStr;
    NSString *totalStr;
    
    float minValue, maxValue;
	float currentValue;  
    
    float percent;
    
    UIImageView *bottomView;
    UIImageView *topView;
}
@property (nonatomic,readwrite) float minValue, maxValue, currentValue;
@property (nonatomic,strong) UILabel *currentLabel,*totalLabel;
@property (nonatomic,strong) NSString *currentStr,*totalStr;

@property (nonatomic,unsafe_unretained)id<SliderDelegate> delegate; 

@end

@protocol SliderDelegate <NSObject>

-(void)scrollBegin:(Slider *)sender;
-(void)scroll:(Slider *)sender;
-(void)scrollDone:(Slider*)sender;

@end

2、在实现的时候,需要掌握三种情况的计算,最左边、最右边、中间位置。因为那个小箭头是需要实时的移动的,不同的位置小箭头的上面所谓的时间view移动的不同。

//
//  Slider.m
//  SliderPro
//
//  Created by Yongchao Zhang on 12-9-13.
//  Copyright (c) 2012年 Yongchao Zhang. All rights reserved.
//

#import "Slider.h"
#import <QuartzCore/QuartzCore.h>

@implementation Slider

@synthesize currentValue,maxValue,minValue;
@synthesize currentLabel,totalLabel;
@synthesize currentStr,totalStr;
@synthesize delegate;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        minValue = 0;
        maxValue = 1;
        currentValue = 0; 
        self.backgroundColor = [UIColor redColor];
        
        self.userInteractionEnabled = YES;
    }
    return self;
}


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code  
    //上半部分整体
    popView= [[UIImageView alloc] init];
    popView.userInteractionEnabled = NO;
//    [popView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"timeBack.png"]]];
    [popView setImage:[UIImage imageNamed:@"timeBack.png"]];
    [self addSubview:popView];
    //时间部分
    UIView *timeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, popView.frame.size.width, self.frame.size.height/2 - 20)];
    [timeView setBackgroundColor:[UIColor greenColor]];
    currentLabel = [[UILabel alloc]init];
    [currentLabel setBackgroundColor:[UIColor blackColor]];
    [currentLabel setTextColor:[UIColor whiteColor]]; 
    [currentLabel setTextAlignment:UITextAlignmentLeft];
    [timeView addSubview:currentLabel]; 
    
    totalLabel = [[UILabel alloc]init];
    [totalLabel setBackgroundColor:[UIColor greenColor]];
//    [totalLabel setTextColor:UIColorFromRGB(0x73736f)];
    [totalLabel setTextColor:[UIColor redColor]];
    [totalLabel setTextAlignment:UITextAlignmentLeft];
    [timeView addSubview:totalLabel]; 

    [popView addSubview:timeView];
    //箭头部分
    tagView = [[UIImageView alloc] init];
    tagView.userInteractionEnabled = NO;
//    [tagView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"tag.png"]]];
    [tagView setImage:[UIImage imageNamed:@"tag.png"]];
    [self addSubview:tagView];
        
    //下半部分 
    UIImage *stetchLeftTrack = [UIImage imageNamed:@"done.png"] ;
    UIImage *stetchRightTrack = [UIImage imageNamed:@"max.png"] ; 
    
    bottomView= [[UIImageView alloc] initWithImage:stetchRightTrack];
    [bottomView setFrame:CGRectMake(5, popView.frame.origin.y + popView.frame.size.height + tagView.frame.size.height+20 , self.frame.size.width-10, 6)];
    CALayer *layers = [bottomView layer];
    [layers setMasksToBounds:YES];
    [layers setCornerRadius:4.0];
    
    topView= [[UIImageView alloc] initWithImage:stetchLeftTrack];
    [topView setFrame:CGRectMake(0, 0 , self.frame.size.width-10, 6)];
     
    [bottomView addSubview:topView];
    [self addSubview:bottomView];
   
}

-(void)layoutSubviews{
    [popView setFrame:CGRectMake(0, 0, 72, self.frame.size.height/2-18)];
    [tagView setFrame:CGRectMake(0, popView.frame.origin.y + popView.frame.size.height-1, 6, 3)]; 
    
    [currentLabel setFrame:CGRectMake(4, 2.0f, 30, 10)];
    currentLabel.font=[UIFont systemFontOfSize:7.0f];
    [totalLabel setFrame:CGRectMake(38, 2.0f, 30, 10)];
    totalLabel.font=[UIFont systemFontOfSize:7.0f];
    
    currentLabel.text = currentStr;
    totalLabel.text = totalStr;
    
} 

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    UITouch* touch = [touches anyObject];
    CGPoint xy = [touch locationInView:self]; 
    float pointX =  xy.x-bottomView.frame.origin.x;
    if(pointX<0)
        pointX =0;
    if(pointX > bottomView.frame.size.width)
        pointX = bottomView.frame.size.width;
    percent = pointX / bottomView.frame.size.width;
    currentValue = percent *maxValue;
    
    
    [self updateProView:pointX]; 
    [self.delegate scrollBegin:self];
}
 
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch* touch = [touches anyObject];
    CGPoint xy = [touch locationInView:self]; 
    float pointX =    xy.x-bottomView.frame.origin.x;
    if(pointX<0)
        pointX =0;
    if(pointX > bottomView.frame.size.width)
        pointX = bottomView.frame.size.width;
    percent = pointX / bottomView.frame.size.width;
    currentValue = percent *maxValue;
    
    [self updateProView:pointX];
    [self.delegate scroll:self];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch* touch = [touches anyObject];
    CGPoint xy = [touch locationInView:self];
    float pointX =    xy.x-bottomView.frame.origin.x;
    if(pointX<0)
        pointX =0;
    if(pointX > bottomView.frame.size.width)
        pointX = bottomView.frame.size.width;
    
    percent = pointX / bottomView.frame.size.width;
    currentValue = percent *maxValue;
    [self updateProView:pointX]; 
    [self.delegate scrollDone:self];
}

-(void)updateProView:(float)_pointx{
    CGRect rect = topView.frame;
    rect.size.width = _pointx;
    topView.frame = rect; 
    
    float realX =  _pointx + bottomView.frame.origin.x;
    CGRect popRect = popView.frame;
    popRect.origin.x = realX-popRect.size.width/2; 
    
    if(realX < popRect.size.width/2){
        popRect.origin.x = 0; 
    }
    if(realX >self.frame.size.width - popRect.size.width/2){
        popRect.origin.x = self .frame.size.width - popRect.size.width;
    
    }
    
    CGRect sanRect = tagView.frame;
    sanRect.origin.x = realX - sanRect.size.width/2;
    
    if(sanRect.origin.x < (bottomView.frame.origin.x - sanRect.size.width/2) )
        sanRect.origin.x = bottomView.frame.origin.x - sanRect.size.width/2;
    if(sanRect.origin.x > (bottomView.frame.origin.x +bottomView.frame.size.width - sanRect.size.width/2))
        sanRect.origin.x =   bottomView.frame.origin.x +bottomView.frame.size.width - sanRect.size.width/2;
    
    
    tagView.frame = sanRect;
     popView.frame = popRect;
}
 
-(void)setMinValue:(float)_minValue{
    minValue = _minValue; 
}

-(void)setMaxValue:(float)_maxValue{
    maxValue = _maxValue; 
}

-(void)setCurrentValue:(float)_value{
    currentValue = _value;
    percent = currentValue /maxValue; 
    float pointX = percent * bottomView.frame.size.width;
    [self updateProView:pointX];
     
}
 
 
@end
注:代码比较乱,也难免有错误,毕竟只是为了验证思路而已。

classdef AudioProcessor < matlab.apps.AppBase % 属性定义(存储音频数据和参数) properties (Access = public) UIFigure matlab.ui.Figure ImportButton matlab.ui.control.Button ProcessButton matlab.ui.control.Button SaveButton matlab.ui.control.Button GainSlider matlab.ui.control.Slider GainLabel matlab.ui.control.Label DelaySlider matlab.ui.control.Slider DelayLabel matlab.ui.control.Label EchoAmpSlider matlab.ui.control.Slider EchoAmpLabel matlab.ui.control.Label NoiseSlider matlab.ui.control.Slider NoiseLabel matlab.ui.control.Label WaveformAxes1 matlab.ui.control.UIAxes WaveformAxes2 matlab.ui.control.UIAxes SpectrumAxes1 matlab.ui.control.UIAxes SpectrumAxes2 matlab.ui.control.UIAxes StatusLabel matlab.ui.control.Label OriginalAudio % 原始音频数据 ProcessedAudio % 处理后音频数据 SampleRate % 采样率 IsMono % 是否单声道 end % 构造函数(初始化界面) methods (Access = private) function createComponents(app) % 创建主窗口 app.UIFigure = uifigure('Visible', 'off'); app.UIFigure.Position = [100, 100, 800, 600]; app.UIFigure.Name = '音频处理系统'; % 导入按钮 app.ImportButton = uibutton(app.UIFigure, 'push'); app.ImportButton.Position = [20, 540, 100, 30]; app.ImportButton.Text = '导入音频'; app.ImportButton.ButtonPushedFcn = @(src, event) app.ImportButtonPushed(src, event); % 处理按钮 app.ProcessButton = uibutton(app.UIFigure, 'push'); app.ProcessButton.Position = [140, 540, 100, 30]; app.ProcessButton.Text = '开始处理'; app.ProcessButton.ButtonPushedFcn = @(src, event) app.ProcessButtonPushed(src, event); app.ProcessButton.Enable = 'off'; % 保存按钮 app.SaveButton = uibutton(app.UIFigure, 'push'); app.SaveButton.Position = [260, 540, 100, 30]; app.SaveButton.Text = '保存结果'; app.SaveButton.ButtonPushedFcn = @(src, event) app.SaveButtonPushed(src, event); app.SaveButton.Enable = 'off'; % 增益滑块 app.GainLabel = uilabel(app.UIFigure); app.GainLabel.Position = [400, 545, 50, 22]; app.GainLabel.Text = '增益(dB)'; app.GainSlider = uislider(app.UIFigure); app.GainSlider.Position = [460, 540, 200, 30]; app.GainSlider.Limits = [0, 12]; app.GainSlider.Value = 6; app.GainSlider.MajorTicks = [0, 6, 12]; app.GainSlider.MajorTickLabels = {'0', '6', '12'}; % 延迟滑块 app.DelayLabel = uilabel(app.UIFigure); app.DelayLabel.Position = [400, 515, 50, 22]; app.DelayLabel.Text = '延迟(ms)'; app.DelaySlider = uislider(app.UIFigure); app.DelaySlider.Position = [460, 510, 200, 30]; app.DelaySlider.Limits = [0, 500]; app.DelaySlider.Value = 300; app.DelaySlider.MajorTicks = [0, 250, 500]; app.DelaySlider.MajorTickLabels = {'0', '250', '500'}; % 回音衰减滑块 app.EchoAmpLabel = uilabel(app.UIFigure); app.EchoAmpLabel.Position = [400, 485, 80, 22]; app.EchoAmpLabel.Text = '回音衰减系数'; app.EchoAmpSlider = uislider(app.UIFigure); app.EchoAmpSlider.Position = [460, 480, 200, 30]; app.EchoAmpSlider.Limits = [0, 1]; app.EchoAmpSlider.Value = 0.5; app.EchoAmpSlider.MajorTicks = [0, 0.5, 1]; app.EchoAmpSlider.MajorTickLabels = {'0', '0.5', '1'}; % 噪声幅度滑块 app.NoiseLabel = uilabel(app.UIFigure); app.NoiseLabel.Position = [400, 455, 80, 22]; app.NoiseLabel.Text = '噪声幅度系数'; app.NoiseSlider = uislider(app.UIFigure); app.NoiseSlider.Position = [460, 450, 200, 30]; app.NoiseSlider.Limits = [0, 0.2]; app.NoiseSlider.Value = 0.1; app.NoiseSlider.MajorTicks = [0, 0.1, 0.2]; app.NoiseSlider.MajorTickLabels = {'0', '0.1', '0.2'}; % 波形图坐标轴1(原始立体声) app.WaveformAxes1 = uiaxes(app.UIFigure); app.WaveformAxes1.Position = [20, 320, 760, 180]; app.WaveformAxes1.Title = '原始音频(立体声)'; app.WaveformAxes1.XLabel = '时间(s)'; app.WaveformAxes1.YLabel = '振幅'; % 波形图坐标轴2(单声道) app.WaveformAxes2 = uiaxes(app.UIFigure); app.WaveformAxes2.Position = [20, 120, 760, 180]; app.WaveformAxes2.Title = '单声道音频'; app.WaveformAxes2.XLabel = '时间(s)'; app.WaveformAxes2.YLabel = '振幅'; % 频谱图坐标轴(可扩展) app.SpectrumAxes1 = uiaxes(app.UIFigure); app.SpectrumAxes1.Position = [20, 320, 760, 180]; app.SpectrumAxes1.Visible = 'off'; % 初始隐藏,处理后显示 app.SpectrumAxes2 = uiaxes(app.UIFigure); app.SpectrumAxes2.Position = [20, 120, 760, 180]; app.SpectrumAxes2.Visible = 'off'; % 状态标签 app.StatusLabel = uilabel(app.UIFigure); app.StatusLabel.Position = [20, 50, 760, 22]; app.StatusLabel.Text = '请点击"导入音频"开始'; app.StatusLabel.HorizontalAlignment = 'center'; % 显示窗口 app.UIFigure.Visible = 'on'; end end % 构造函数 methods (Access = public) function app = AudioProcessor % 创建组件 createComponents(app); end end % 导入音频按钮回调 methods (Access = private) function ImportButtonPushed(app, ~) % 打开文件选择对话框 [filePath, fileName] = uigetfile({'*.wav', 'WAV音频文件 (*.wav)'}); if filePath == 0 return; % 未选择文件 end try % 读取音频文件 [y, Fs] = audioread(fullfile(filePath, fileName)); app.OriginalAudio = y; app.SampleRate = Fs; app.IsMono = size(y, 2) == 1; % 显示原始波形 plotOriginalWaveforms(app, y, Fs); % 启用处理按钮 app.ProcessButton.Enable = 'on'; app.StatusLabel.Text = ['已导入: ', fileName]; catch e uialert(app.UIFigure, ['读取失败: ', e.message], '错误'); end end % 开始处理按钮回调 function ProcessButtonPushed(app, ~) if isempty(app.OriginalAudio) uialert(app.UIFigure, '请先导入音频', '提示'); return; end y = app.OriginalAudio; Fs = app.SampleRate; % 1. 双声道转单声道 if ~app.IsMono y_mono = mean(y, 2); else y_mono = y; end % 2. 增益调节(dB转线性因子) gain_db = app.GainSlider.Value; gain_factor = 10^(gain_db/20); % +6dB = 2倍,+12dB=4倍 y_gain = y_mono * gain_factor; % 3. 混音处理(添加白噪声) noise_amp = app.NoiseSlider.Value; noise = noise_amp * randn(size(y_gain)); y_mix = y_gain + noise; % 4. 回音效果 echo_delay_ms = app.DelaySlider.Value; echo_delay = echo_delay_ms / 1000; % 转换为秒 echo_amp = app.EchoAmpSlider.Value; y_echo = addEcho(y_mix, Fs, echo_delay, echo_amp); % 5. 减抽样处理 new_Fs = Fs/2; % 半抽样率 y_down = resample(y_echo, new_Fs, Fs); % 防混叠重采样 % 6. IIR低通滤波器设计 fc = 4000; % 截止频率4kHz [b, a] = butter(4, fc/(new_Fs/2), 'low'); % 4阶巴特沃斯滤波器 y_filtered = filter(b, a, y_down); % 保存处理结果 app.ProcessedAudio = y_filtered; % 显示处理后的波形和频谱 plotProcessedResults(app, y_mono, y_filtered, Fs, new_Fs); % 启用保存按钮 app.SaveButton.Enable = 'on'; app.StatusLabel.Text = '处理完成!'; end % 保存结果按钮回调 function SaveButtonPushed(app, ~) if isempty(app.ProcessedAudio) uialert(app.UIFigure, '无处理结果可保存', '提示'); return; end [filePath, fileName] = uiputfile({'*.wav', 'WAV文件 (*.wav)'}, ... '保存处理后的音频', 'processed_audio.wav'); if filePath == 0 return; % 未选择保存 end try audiowrite(fullfile(filePath, fileName), app.ProcessedAudio, app.SampleRate/2); uialert(app.UIFigure, ['已保存至: ', fileName], '成功'); catch e uialert(app.UIFigure, ['保存失败: ', e.message], '错误'); end end % 绘制原始波形 function plotOriginalWaveforms(app, y_stereo, Fs) t = (0:length(y_stereo)-1)/Fs; % 显示原始立体声波形 cla(app.WaveformAxes1); if size(y_stereo, 2) == 2 plot(app.WaveformAxes1, t, y_stereo(:,1), 'b', t, y_stereo(:,2), 'r'); legend(app.WaveformAxes1, '左声道', '右声道'); else plot(app.WaveformAxes1, t, y_stereo); legend(app.WaveformAxes1, '单声道'); end title(app.WaveformAxes1, '原始音频波形'); xlabel(app.WaveformAxes1, '时间(s)'); ylabel(app.WaveformAxes1, '振幅'); grid(app.WaveformAxes1, 'on'); % 显示单声道波形(若为立体声) if size(y_stereo, 2) == 2 y_mono = mean(y_stereo, 2); cla(app.WaveformAxes2); plot(app.WaveformAxes2, t, y_mono); title(app.WaveformAxes2, '单声道转换后波形'); xlabel(app.WaveformAxes2, '时间(s)'); ylabel(app.WaveformAxes2, '振幅'); grid(app.WaveformAxes2, 'on'); else app.WaveformAxes2.Visible = 'off'; end end % 绘制处理结果(波形和频谱) function plotProcessedResults(app, y_orig, y_proc, Fs, new_Fs) % 隐藏原始波形,显示频谱 app.WaveformAxes1.Visible = 'off'; app.WaveformAxes2.Visible = 'off'; app.SpectrumAxes1.Visible = 'on'; app.SpectrumAxes2.Visible = 'on'; % 绘制原始音频频谱 plotSpectrum(app, y_orig, Fs, app.SpectrumAxes1); title(app.SpectrumAxes1, '原始音频频谱'); % 绘制处理后音频频谱 plotSpectrum(app, y_proc, new_Fs, app.SpectrumAxes2); title(app.SpectrumAxes2, '处理后音频频谱'); end % 频谱分析函数 function plotSpectrum(app, y, Fs, axesHandle) N = 2^nextpow2(length(y)); f = Fs*(0:(N/2))/N; Y = fft(y, N); P = abs(Y/N).^2; % 功率谱密度 cla(axesHandle); plot(axesHandle, f, 10*log10(P(1:N/2+1))); % 转换为dB xlabel(axesHandle, '频率(Hz)'); ylabel(axesHandle, '功率谱密度(dB/Hz)'); xlim(axesHandle, [0, Fs/2]); grid(axesHandle, 'on'); end end % 核心音频处理函数(与原逻辑一致) methods (Access = private) function y_out = addEcho(y_in, Fs, delay, amp) delay_samples = round(delay * Fs); y_out = y_in; % 避免数组越界 max_idx = length(y_in) - delay_samples; if max_idx > 0 y_out(delay_samples+1:end) = y_out(delay_samples+1:end) + amp * y_in(1:max_idx); end end end end
06-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值