当前位置: 首页 > 编程日记 > 正文

MBProgressHUD 使用详解

MBProgressHUD是一个显示HUD窗口的第三方类库,用于在执行一些后台任务时,在程序中显示一个表示进度的loading视图和两个可选的文本提示的HUD窗口。我想最多是应用在加载网络数据的时候。其实苹果官方自己有一个带有此功能的类UIProgressHUD,只不过它是私有的,现在不让用。至于实际的效果,可以看看github上工程给出的几张图例(貌似我这经常无法单独打开图片,所以就不在这贴图片了),也可以运行一下Demo。

具体用法我们就不多说了,参考github上的说明就能用得很顺的。本文主要还是从源码的角度来分析一下它的具体实现。

模式

在分析实现代码之前,我们先来看看MBProgressHUD中定义的MBProgressHUDMode枚举。它用来表示HUD窗口的模式,即我们从效果图中看到的几种显示样式。其具体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef enum {
    // 使用UIActivityIndicatorView来显示进度,这是默认值
    MBProgressHUDModeIndeterminate,
    // 使用一个圆形饼图来作为进度视图
    MBProgressHUDModeDeterminate,
    // 使用一个水平进度条
    MBProgressHUDModeDeterminateHorizontalBar,
    // 使用圆环作为进度条
    MBProgressHUDModeAnnularDeterminate,
    // 显示一个自定义视图,通过这种方式,可以显示一个正确或错误的提示图
    MBProgressHUDModeCustomView,
    // 只显示文本
    MBProgressHUDModeText
} MBProgressHUDMode;

通过设置MBProgressHUD的模式,我们可以使用MBProgressHUD自定义的表示进度的视图来满足我们的需求,也可以自定义这个进度视图,当然还可以只显示文本。在下面我们会讨论源码中是如何使用这几个值的。

外观

我们先来了解一下MBProgressHUD的基本组成。一个MBProgressHUD视图主要由四个部分组成:

  1. loading动画视图(在此做个统称,当然这个区域可以是自定义的一个UIImageView视图)。这个视图由我们设定的模式值决定,可以是菊花、进度条,也可以是我们自定义的视图;

  2. 标题文本框(label):主要用于显示提示的主题信息。这个文本框是可选的,通常位于loading动画视图的下面,且它是单行显示。它会根据labelText属性来自适应文本的大小(有一个长度上限),如果过长,则超出的部分会显示为”…“;

  3. 详情文本框(detailsLabel)。如果觉得标题不够详细,或者有附属信息,就可以将详细信息放在这里面显示。该文本框对应的是显示detailsLabelText属性的值,它是可以多行显示的。另外,详情的显示还依赖于labelText属性的设置,只有labelText属性被设置了,且不为空串,才会显示detailsLabel;

  4. HUD背景框。主要是作为上面三个部分的一个背景,用来突出上面三部分。

为了让我们更好地自定义这几个部分,MBProgressHUD还提供了一些属性,我们简单了解一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 背景框的透明度,默认值是0.8
@property (assign) float opacity;
// 背景框的颜色
// 需要注意的是如果设置了这个属性,则opacity属性会失效,即不会有半透明效果
@property (MB_STRONG) UIColor *color;
// 背景框的圆角半径。默认值是10.0
@property (assign) float cornerRadius;
// 标题文本的字体及颜色
@property (MB_STRONG) UIFont* labelFont;
@property (MB_STRONG) UIColor* labelColor;
// 详情文本的字体及颜色
@property (MB_STRONG) UIFont* detailsLabelFont;
@property (MB_STRONG) UIColor* detailsLabelColor;
// 菊花的颜色,默认是白色
@property (MB_STRONG) UIColor *activityIndicatorColor;

通过以上属性,我们可以根据自己的需要来设置这几个部分的外观。

另外还有一个比较有意思的属性是dimBackground,用于为HUD窗口的视图区域覆盖上一层径向渐变(radial gradient)层,其定义如下:

1
@property (assign) BOOL dimBackground;

让我们来看看通过它,MBProgressHUD都做了些什么。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (void)drawRect:(CGRect)rect {
    ...
    if (self.dimBackground) {
        //Gradient colours
        size_t gradLocationsNum = 2;
        CGFloat gradLocations[2] = {0.0f, 1.0f};
        CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; 
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum);
        CGColorSpaceRelease(colorSpace);
        //Gradient center
        CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
        //Gradient radius
        float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ;
        // 由中心向四周绘制渐变
        CGContextDrawRadialGradient (context, gradient, gradCenter,
                                     0, gradCenter, gradRadius,
                                     kCGGradientDrawsAfterEndLocation);
        CGGradientRelease(gradient);
    }
    ... 
}

这段代码由中心向MBProgressHUD视图的四周绘制了一个渐变层。当然,这里的颜色值是写死的,我们无法自行定义。有兴趣的话,大家可以将这个属性设置为YES,看看实际的效果。

创建、布局与绘制

除了继承自UIView的-initWithFrame:初始化方法,MBProgressHUD还为我们提供了两个初始化方法,如下所示:

1
2
3
- (id)initWithWindow:(UIWindow *)window;
- (id)initWithView:(UIView *)view;

这两个方法分别传入一个UIWindow对象和一个UIView对象。传入的视图对象仅仅是做为MBProgressHUD视图定义其frame属性的参照,而不会直接将MBProgressHUD视图添加到传入的视图对象上。这个添加操作还得我们自行处理(当然,MBProgressHUD还提供了几个便捷的类方法,我们下面会说明)。

MBProgressHUD提供了几个属性,可以让我们控制HUD的布局,这些属性主要有以下几个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// HUD相对于父视图中心点的x轴偏移量和y轴偏移量
@property (assign) float xOffset;
@property (assign) float yOffset;
// HUD各元素与HUD边缘的间距
@property (assign) float margin;
// HUD背景框的最小大小
@property (assign) CGSize minSize;
// HUD的实际大小
@property (atomic, assign, readonly) CGSize size;
// 是否强制HUD背景框宽高相等
@property (assign, getter = isSquare) BOOL square;

需要注意的是,MBProgressHUD视图会充满其父视图的frame内,为此,在MBProgressHUD的layoutSubviews方法中,还专门做了处理,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
- (void)layoutSubviews {
    [super layoutSubviews];
    // Entirely cover the parent view
    UIView *parent = self.superview;
    if (parent) {
        self.frame = parent.bounds;
    }
    ...
}

也因此,当MBProgressHUD显示时,它也会屏蔽父视图的各种交互操作。

在布局的过程中,会先根据我们要显示的视图计算出容纳这些视图所需要的总的宽度和高度。当然,会设置一个最大值。我们截取其中一段来看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CGRect bounds = self.bounds;
...
CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin; 
CGSize maxSize = CGSizeMake(maxWidth, remainingHeight);
CGSize detailsLabelSize = MB_MULTILINE_TEXTSIZE(detailsLabel.text, detailsLabel.font, maxSize, detailsLabel.lineBreakMode);
totalSize.width = MAX(totalSize.width, detailsLabelSize.width);
totalSize.height += detailsLabelSize.height;
if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) {
    totalSize.height += kPadding;
}
totalSize.width += 2 * margin;
totalSize.height += 2 * margin;

之后,就开始从上到下放置各个视图。在布局代码的最后,计算了一个size值,这是为后面绘制背景框做准备的。

在上面的布局代码中,主要是处理了loading动画视图、标题文本框和详情文本框,而HUD背景框主要是在drawRect:中来绘制的。背景框的绘制代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Center HUD
CGRect allRect = self.bounds;
// Draw rounded HUD backgroud rect
CGRect boxRect = CGRectMake(round((allRect.size.width - size.width) / 2) + self.xOffset,
                            round((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height);
float radius = self.cornerRadius;
CGContextBeginPath(context);
CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect));
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0);
CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0);
CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0);
CGContextClosePath(context);
CGContextFillPath(context);

这是最平常的绘制操作,在此不多做解释。

我们上面讲过MBProgressHUD提供了几种窗口模式,这几种模式的主要区别在于loading动画视图的展示。默认情况下,使用的是菊花(MBProgressHUDModeIndeterminate)。我们可以通过设置以下属性,来改变loading动画视图:

1
@property (assign) MBProgressHUDMode mode;

对于其它几种模式,MBProgressHUD专门我们提供了几个视图类。如果是进度条模式(MBProgressHUDModeDeterminateHorizontalBar),则使用的是MBBarProgressView类;如果是饼图模式(MBProgressHUDModeDeterminate)或环形模式(MBProgressHUDModeAnnularDeterminate),则使用的是MBRoundProgressView类。上面这两个类的主要操作就是在drawRect:中根据一些进度参数来绘制形状,大家可以自己详细看一下。

当然,我们还可以自定义loading动画视图,此时选择的模式是MBProgressHUDModeCustomView。或者不显示loading动画视图,而只显示文本框(MBProgressHUDModeText)。

具体显示哪一种loading动画视图,是在-updateIndicators方法中来处理的,其实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)updateIndicators {
    BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
    BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];
    if (mode == MBProgressHUDModeIndeterminate) {
        ...
    }
    else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
        // Update to bar determinate indicator
        [indicator removeFromSuperview];
        self.indicator = MB_AUTORELEASE([[MBBarProgressView alloc] init]);
        [self addSubview:indicator];
    }
    else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
        if (!isRoundIndicator) {
            ...
        }
        if (mode == MBProgressHUDModeAnnularDeterminate) {
            [(MBRoundProgressView *)indicator setAnnular:YES];
        }
    
    else if (mode == MBProgressHUDModeCustomView && customView != indicator) {
        ...
    else if (mode == MBProgressHUDModeText) {
        ...
    }
}

显示与隐藏

MBRoundProgressView为我们提供了丰富的显示与隐藏HUD窗口的。在分析这些方法之前,我们先来看看MBProgressHUD为显示与隐藏提供的一些属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// HUD显示和隐藏的动画类型
@property (assign) MBProgressHUDAnimation animationType;
// HUD显示的最短时间。设置这个值是为了避免HUD显示后立即被隐藏。默认值为0
@property (assign) float minShowTime;
// 这个属性设置了一个宽限期,它是在没有显示HUD窗口前被调用方法可能运行的时间。
// 如果被调用方法在宽限期内执行完,则HUD不会被显示。
// 这主要是为了避免在执行很短的任务时,去显示一个HUD窗口。
// 默认值是0。只有当任务状态是已知时,才支持宽限期。具体我们看实现代码。
@property (assign) float graceTime;
// 这是一个标识位,标明执行的操作正在处理中。这个属性是配合graceTime使用的。
// 如果没有设置graceTime,则这个标识是没有太大意义的。在使用showWhileExecuting:onTarget:withObject:animated:方法时,
// 会自动去设置这个属性为YES,其它情况下都需要我们自己手动设置。
@property (assign) BOOL taskInProgress;
// 隐藏时是否将HUD从父视图中移除,默认是NO。
@property (assign) BOOL removeFromSuperViewOnHide;
// 进度指示器,从0.0到1.0,默认值为0.0
@property (assign) float progress;
// 在HUD被隐藏后的回调
@property (copy) MBProgressHUDCompletionBlock completionBlock;

以上这些属性都还好理解,可能需要注意的就是graceTime和taskInProgress的配合使用。在下面我们将会看看这两个属性的用法。

对于显示操作,最基本的就是-show:方法(其它几个显示方法都会调用该方法来显示HUD窗口),我们先来看看它的实现,

1
2
3
4
5
6
7
8
9
10
11
12
- (void)show:(BOOL)animated {
    useAnimation = animated;
    // If the grace time is set postpone the HUD display
    if (self.graceTime > 0.0) {
        self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self 
                           selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
    
    // ... otherwise show the HUD imediately 
    else {
        [self showUsingAnimation:useAnimation];
    }
}

可以看到,如果我们没有设置graceTime属性,则会立即显示HUD;而如果设置了graceTime,则会创建一个定时器,并让显示操作延迟到graceTime所设定的时间再执行,而-handleGraceTimer:实现如下:

1
2
3
4
5
6
- (void)handleGraceTimer:(NSTimer *)theTimer {
    // Show the HUD only if the task is still running
    if (taskInProgress) {
        [self showUsingAnimation:useAnimation];
    }
}

可以看到,只有在设置了taskInProgress标识位为YES的情况下,才会去显示HUD窗口。所以,如果我们要自己调用-show:方法的话,需要酌情考虑设置taskInProgress标识位。

除了-show:方法以外,MBProgressHUD还为我们提供了一组显示方法,可以让我们在显示HUD的同时,执行一些后台任务,我们在此主要介绍两个。其中一个是-showWhileExecuting:onTarget:withObject:animated:,它是基于target-action方式的调用,在执行一个后台任务时显示HUD,等后台任务执行完成后再隐藏HUD,具体实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
    methodForExecution = method;
    targetForExecution = MB_RETAIN(target);
    objectForExecution = MB_RETAIN(object); 
    // Launch execution in new thread
    self.taskInProgress = YES;
    [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil];
    // Show HUD view
    [self show:animated];
}
- (void)launchExecution {
    @autoreleasepool {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        // Start executing the requested task
        [targetForExecution performSelector:methodForExecution withObject:objectForExecution];
#pragma clang diagnostic pop
        // Task completed, update view in main thread (note: view operations should
        // be done only in the main thread)
        [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO];
    }
}

可以看到,-showWhileExecuting:onTarget:withObject:animated:首先将taskInProgress属性设置为YES,这样在调用-show:方法时,即使设置了graceTime,也确保能在任务完成之前显示HUD。然后开启一个新线程,来异步执行我们的后台任务,最后去显示HUD。

而在异步调用方法-launchExecution中,线程首先是维护了自己的一个@autoreleasepool,所以在我们自己的方法中,就不需要再去维护一个@autoreleasepool了。之后是去执行我们的任务,在任务完成之后,再回去主线程去执行清理操作,并隐藏HUD窗口。

另一个显示方法是-showAnimated:whileExecutingBlock:onQueue:completionBlock:,它是基于GCD的调用,当block中的任务在指定的队列中执行时,显示HUD窗口,任务完成之后执行completionBlock操作,最后隐藏HUD窗口。我们来看看它的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
     completionBlock:(MBProgressHUDCompletionBlock)completion {
    self.taskInProgress = YES;
    self.completionBlock = completion;
    dispatch_async(queue, ^(void) {
        block();
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            [self cleanUp];
        });
    });
    [self show:animated];
}

这个方法也是首先将taskInProgress属性设置为YES,然后开启一个线程去执行block任务,最后主线程去执行清理操作,并隐藏HUD窗口。

对于HUD的隐藏,MBProgressHUD提供了两个方法,一个是-hide:,另一个是-hide:afterDelay:,后者基于前者,所以我们主要来看看-hide:的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)hide:(BOOL)animated {
    useAnimation = animated;
    // If the minShow time is set, calculate how long the hud was shown,
    // and pospone the hiding operation if necessary
    if (self.minShowTime > 0.0 && showStarted) {
        NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted];
        if (interv < self.minShowTime) {
            self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self 
                                selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
            return;
        
    }
    // ... otherwise hide the HUD immediately
    [self hideUsingAnimation:useAnimation];
}

我们可以看到,在设置了minShowTime属性并且已经显示了HUD窗口的情况下,会去判断显示的时间是否小于minShowTime指定的时间,如果是,则会开启一个定时器,等到显示的时间到了minShowTime所指定的时间,才会去隐藏HUD窗口;否则会直接去隐藏HUD窗口。

隐藏的实际操作主要是去做了些清理操作,包括根据设定的removeFromSuperViewOnHide值来执行是否从父视图移除HUD窗口,以及执行completionBlock操作,还有就是执行代理的hudWasHidden:方法。这些操作是在私有方法-done里面执行的,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)done {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    isFinished = YES;
    self.alpha = 0.0f;
    if (removeFromSuperViewOnHide) {
        [self removeFromSuperview];
    }
#if NS_BLOCKS_AVAILABLE
    if (self.completionBlock) {
        self.completionBlock();
        self.completionBlock = NULL;
    }
#endif
    if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
        [delegate performSelector:@selector(hudWasHidden:) withObject:self];
    }
}

其它

MBProgressHUD的一些主要的代码差不多已经分析完了,最后还有些边边角角的地方,一起来看看。

显示和隐藏的便捷方法

除了上面描述的实例方法之外,MBProgressHUD还为我们提供了几个便捷显示和隐藏HUD窗口的方法,如下所示:

1
2
3
4
5
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view animated:(BOOL)animated
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated
+ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated

方法的签名已经很能说明问题了,在此不多描述。

部分属性值的设置

对于部分属性(主要是”外观”一节中针对菊花、标题文本框和详情文本框的几个属性值),为了在设置将这些属性时修改对应视图的属性,并没有直接为每个属性生成一个setter,而是通过KVO来监听这些属性值的变化,再将这些值赋值给视图的对应属性,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 监听的属性数组
- (NSArray *)observableKeypaths {
    return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", @"labelColor", ..., nil];
}
// 注册KVO
- (void)registerForKVO {
    for (NSString *keyPath in [self observableKeypaths]) {
        [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL];
    }
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO];
    else {
        [self updateUIForKeypath:keyPath];
    }
}
- (void)updateUIForKeypath:(NSString *)keyPath {
    if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"] ||
        [keyPath isEqualToString:@"activityIndicatorColor"]) {
        [self updateIndicators];
    else if ([keyPath isEqualToString:@"labelText"]) {
        label.text = self.labelText;
    
    ...
    [self setNeedsLayout];
    [self setNeedsDisplay];
}

代理

MBProgressHUD还为我们提供了一个代理MBProgressHUDDelegate,这个代理中只提供了一个方法,即:

1
- (void)hudWasHidden:(MBProgressHUD *)hud;

这个代理方法是在隐藏HUD窗口后调用,如果此时我们需要在我们自己的实现中执行某些操作,则可以实现这个方法。

问题

MBProgressHUD为我们提供了一个HUD窗口的很好的实现,不过个人在使用过程中,觉得它给我们提供的交互功能太少。其代理只提供了一个-hudWasHidden:方法,而且我们也无法通过点击HUD来执行一些操作。在现实的需求中,可能存在这种情况:比如一个网络操作,在发送请求等待响应的过程中,我们会显示一个HUD窗口以显示一个loading框。但如果我们想在等待响应的过程中,在当前视图中取消这个网络请求,就没有相应的处理方式,MBProgressHUD没有为我们提供这样的交互操作。当然这时候,我们可以根据自己的需求来修改源码。

与SVProgressHUD的对比

与MBProgressHUD类似,SVProgressHUD类库也为我们提供了在视图中显示一个HUD窗口的功能。两者的基本思路是差不多的,差别更多的是在实现细节上。相对于MBProgressHUD来说,SVProgressHUD的实现有以下几点不同:

  1. SVProgressHUD类对外提供的都是类方法,包括显示、隐藏、和视图属性设置都是使用类方法来操作。其内部实现为一个单例对象,类方法实际是针对这个单例对象来操作的。

  2. SVProgressHUD主要包含三部分:loading视图、提示文本框和背景框,没有详情文本框。

  3. SVProgressHUD默认提供了正确、错误和信息三种状态视图(与loading视图同一位置,根据需要来设置)。当然MBProgressHUD中,也可以自定义视图(customView)来显示相应的状态视图。

  4. SVProgressHUD为我们提供了更多的交互操作,包括点击事件、显示事件及隐藏事件。不过这些都是通过通知的形式向外发送,所以我们需要自己去监听这些事件。

  5. SVProgressHUD中一些loading动画是以Layer动画的形式来实现的。

相关文章:

V4L2获取usb视频流测试代码

Video4Linux2(Video for Linux Two, 简称V4L2)是Linux中关于视频设备的驱动框架&#xff0c;为上层访问底层的视频设备提供统一接口。V4L2主要支持三类设备&#xff1a;视频输入输出设备、VBI设备和Radio设备&#xff0c;分别会在/dev目录下产生videoX、vbiX和radioX设备节点&a…

深度学习渐趋冷静,为何图形计算却逆势反涨?

不知不觉&#xff0c;2019 年的进度条已经快撑不住了 ▓▓▓▓▓▓▓▓▓▓▓▓▓░░ 88%。就像这个进度条一样&#xff0c;人工智能的发展也绝不是一蹴而就的事&#xff0c;而是一步一个脚印逐渐发展&#xff0c;最后达成某个目标。近年来&#xff0c;深度学习领域的技术发展…

数字图像处理课设

2019独角兽企业重金招聘Python工程师标准>>> 对于整个窗体的设计&#xff0c;菜单组件&#xff08;MenuStrip)和工具条&#xff08;ToolStrip&#xff09;来添加相应的功能事件当然还有右击鼠标就能弹出相应的属性框&#xff0c;也是使用了叫contextMenuStrip的组件…

Swift3.0带来的变化汇总

var string "Hello-Swift" //获取某个下标后一个下标对应的字符 char"e" //swift2.2 //var char string[startIndex.successor()] //swift3.0 var char string[string.index(after: startIndex)] //获取某个下标前一个下标对应的字符 char2 "t&qu…

通过配置NFS使Ubuntu和海思3559A板子共享目录

之前在Ubuntu和海思3559A板子之间来回拷贝文件都是用的scp命令&#xff0c;不是很方便&#xff0c;这里通过配置NFS来实现它们之间共享目录&#xff0c;操作步骤如下&#xff1a; 1. 在Ubuntu上安装NFS&#xff0c;执行以下命令&#xff0c;执行结果如下&#xff1a; sudo ap…

22w+的人选择了这款蓝牙耳机

01现在有两个电子产品&#xff0c;应该是你们现在必有的&#xff0c;手机和电脑。但还有一样不可缺少的电子产品&#xff0c;那就是耳机。不管喜不喜欢带&#xff0c;反正你手里肯定有一个&#xff0c;应该没人不认可我说的这个话吧。反正我去哪都会带着耳机&#xff0c;听不听…

自定义数字格式字符串输出示例

自定义数字格式字符串输出示例 MSDN 下表阐释了通过给特定数据类型和值应用某些自定义数字格式字符串来创建的输出。输出是通过使用 ToString 方法和美国英语 (en-US) 区域性生成的。 “格式字符串”列指示格式字符串&#xff0c;“数据类型”列指示所用的数据类型&#xff0c;…

海思3559A上编译LIVE555源码操作步骤

1. 从http://www.live555.com/liveMedia/public/ 下载live.2019.06.28.tar.gz&#xff0c;并解压缩&#xff1b; 2. 生成静态库&#xff0c;拷贝一份config.armlinux&#xff0c;取名为config.hi3559a&#xff0c;修改后的内容如下: CROSS_COMPILE? /opt/hisi-linux/x86-ar…

基于海康机器视觉算法平台的对位贴合项目个人理解 | CSDN原力计划

扫码参与CSDN“原力计划”作者 | 果汁分你一半哈哈来源 | CSDN原力计划获奖作品都说“纸上得来终觉浅&#xff0c;绝知此事要躬行”&#xff0c;可惜咱没这条件呀&#xff0c;没项目咱也不能干坐着呀&#xff0c;那咱发挥主观能动性&#xff0c;咱不是学机械的么&#xff0c;还…

解决Swift中present(uiImagePickerController,animated: true,completion: nil)闪退的问题

swift中开发选择图片上传&#xff0c;会使用到Tap Gesture Recognizer控件&#xff0c;对应 UITapGestureRecognizer API&#xff0c;以下是代码示例&#xff08;取自IOS developer library&#xff09;&#xff1a; IBAction func selectImageFromPhotoLibrary(_ sender: UIT…

测试发现equals和hashCode与书上描述的不一样

2019独角兽企业重金招聘Python工程师标准>>> 如果两个对象根据equals()方法比较是相等的&#xff0c;那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。但是现实情况&#xff0c;两者产生的hashCode值却不相等求解&#xff1f; 转载于:htt…

海思3559A上编译OpenCV4.1.0源码操作步骤

1. 从https://github.com/opencv/opencv/releases 下载opencv源码opencv-4.1.0.zip并解压缩&#xff1b; 2. 修改最顶层的CMakeLists.txt&#xff0c;在第658行加入一条语句如下&#xff0c;fix error: pngstruct.h:30:18: fatal error: zlib.h: No such file or directory #i…

如何从零开始设计与开发一款通用模型预测调度系统 | 干货分享

机器学习模型从训练导出到生成环境部署&#xff0c;这个过程中涉及大量工作&#xff0c;会面临着各种问题与挑战&#xff0c;比如不断丰富的业务场景&#xff0c;系统需要负载种类繁多的神经网络&#xff0c;预测任务不均匀等&#xff0c;然而企业拥有的计算资源&#xff08;如…

闲来无事,总结 Xcode常用快捷键

IOS模拟器home键&#xff1a;Command Shift H 构建应用程序&#xff1a;Command &#xff0b; B 运行app&#xff1a;Command &#xff0b; R 清理工程&#xff1a;Command &#xff0b; Shift &#xff0b; K 打开Xcode首选项&#xff1a;Command &#xff0b; &#xff…

openNebulafrontEnd ComputeNode 配置记录

openNebulafrontEnd ComputeNode 配置记录 1,OpenNebula nfs(file system shared) for image datastore; openNebula computeNode挂载,如果frontend与compute在同一台服务器则不用做NFS shared; 3,make raw image 4,配置过程中配置服务器网桥报错 报错信息 bringing up interfa…

武汉大学提出ARGAN:注意力循环生成对抗模型用于检测、去除图像阴影 | ICCV 2019...

作者 | 王红成出品&#xff5c;AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】如何去除一张图像中的阴影部分&#xff1f;在ICCV 2019会上&#xff0c;武汉大学的一篇论文针对这一问题提出了一种用于阴影检测和去除的注意循环生成对抗网络——ARGAN。论…

海思3559A上编译libyuv源码操作步骤

1. 下载libyuv源代码&#xff0c;执行&#xff1a; git clone https://chromium.googlesource.com/libyuv/libyuv 2. 通过CMakeLists.txt编译生成库&#xff0c;build.sh脚本内容如下&#xff1a; cmake \-DCMAKE_BUILD_TYPERELEASE \-DCMAKE_C_COMPILER/opt/hisi-linux/x86…

Cocoapods的安装以及使用(2)

Cocoapods 在网上看博客&#xff0c;看了好多次&#xff0c;都没有学会cocoapods&#xff0c;今天上午浪费了一上午的时间&#xff0c;终于算是学会了。其实也是很简单的。 iOS 新版 CocoaPods 安装流程 1.换掉现有Ruby默认源&#xff08;由于好多人都没有翻墙&#xff0c;所…

MyEclipse10中导入的jquery文件报错(出现红叉叉,提示语法错误)

为了做一个页面特效&#xff0c;导入了一个jquery文件&#xff0c;怎想&#xff0c;myeclipse竟然报错说是语法错误&#xff0c;但是这个js文件我是从官网上下载的&#xff0c;不应该出错才对&#xff0c;百度谷歌之后终于找到了解决办法&#xff1a; 选中报错的js文件&#xf…

海思3559A上编译libjpeg-turbo源码操作步骤

1. 从https://github.com/libjpeg-turbo/libjpeg-turbo/releases/tag/2.0.2 下载libjpeg-turbo 2.0.2版本&#xff1b; 2. 脚本build.sh内容如下&#xff1a; cmake \-DCMAKE_BUILD_TYPERELEASE \-DCMAKE_C_COMPILER/opt/hisi-linux/x86-arm/aarch64-himix100-linux/bin/aarc…

免费!这里有一份开发者进阶“宝典”求带走

作为开发者&#xff0c;无论是前端还是后端&#xff0c;做机器学习还是云计算、架构&#xff0c;保持对技术的敏感性都是非常重要的事。新技术一般发源于人力、财力等各方面资源都很雄厚的大厂&#xff0c;经过时间的沉淀逐渐成为技术主流。因此&#xff0c;从 BAT、Google、Fa…

使用终端建立自己需要工程

cocoapods应该是时下类库比较全面的三方管理工具&#xff0c;使用也特别方便&#xff0c;你只需将别人的类库在你的 podfile中 pod "SomeoneNameLibrary" 就可以在你的工程中使用这个库&#xff0c;作为一个开发者&#xff0c;你是否也想让别人在他的项目中 pod &quo…

1.0 封装后的布局成本

2019独角兽企业重金招聘Python工程师标准>>> 1.0.1 C语言中&#xff08;.c&#xff09; 1. 数据和函数是分开声明的&#xff0c;语言本身没有支持“数据和函数”之间的关联性。 2. 这种程序方法为程序性的&#xff0c;由一组“分布在各个以功能为导向的函数…

通过gdb core dump方法查看程序异常时的堆栈信息

在Linux下可通过core文件来获取当程序异常退出(如异常信号SIGSEGV, SIGABRT等)时的堆栈信息。core dump叫做核心转储&#xff0c;当程序运行过程中发生异常的那一刻的一个内存快照&#xff0c;操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下&#xff0c;会把进程…

日均350000亿接入量,腾讯TubeMQ性能超过Kafka

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】近日&#xff0c;腾讯开源动作不断&#xff0c;相继开源了分布式消息中间件TubeMQ&#xff0c;基于最主流的 OpenJDK8开发的Tencent Kona JDK&#xff0c;分布式HTAP数据库 TBase&#xff0c;企业级…

iOS应用版本更新(自动提醒用户)

在#import "AppDelegate.h" 文件中的application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions 方法中调用检测结果 获得发布版本的Version 比较当前版本与新上线版本做比较 UIAlertView代理方法

Bash Shell脚本编程-变量知识

Shell:GUI CLI提供交互式接口&#xff1a;提高效率命令行展开&#xff1a;~ &#xff0c;{}命令别名&#xff1a;alias命令历史&#xff1a;historyGlobbing&#xff1a;*&#xff0c;&#xff1f;&#xff0c;[]&#xff0c;[^]命令补全&#xff1a;$PATH指定的目录下路径补全…

FFmpeg中可执行文件ffplay用法汇总

从https://ffbinaries.com/downloads 下载最新的4.1版本的windows 64位FFplay。目前linux下的只有3.2版本的。FFplay是一个由FFmpeg和SDL库组成的简单媒体播放器&#xff0c;它主要用作各种FFmpeg API的测试。 通过执行以下命令将FFplay信息重定位到ffplay_help.txt文件中便于…

用Go重构C语言系统,这个抗住春晚红包的百度转发引擎承接了万亿流量

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;11 月 20 日&#xff0c;百度的万亿流量转发引擎 BFE 登上了 GitHub Trending Top 3&#xff0c;今日 Star 已突破 270。事实上&#xff0c;这个曾经抗住 2019 年春晚抢红包的转发引擎早已于 2019 年夏在 G…

Swift3.0带来的变化汇总系列一——字符串与基本运算符中的变化

var string "Hello-Swift" //获取某个下标后一个下标对应的字符 char"e" //swift2.2 //var char string[startIndex.successor()] //swift3.0 var char string[string.index(after: startIndex)] //获取某个下标前一个下标对应的字符 char2 "t&qu…