iOS中UIScrollView、UIWebView、UICollectionView实现图文混排

问题汇总

1、如何实现JavaScript与Objective-C间传值?

点击Webview中的图片,放大,需要JavaScript和Objective-C传值,获取到具体需要放大哪张图片。

本方案中,不需要引入WebViewJavascriptBridge,而是通过【控制Webview重定向方法,拦截发出的请求】来实现。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 每个添加点击事件(window.location.href),其中selectIndex为图片标识// webview发起请求拦截
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
     // 获取img标识index
     NSString *url = request.URL.absoluteString;
     NSRange range = [url rangeOfString:@ "selectIndex=" ];
     if   (range.location != NSNotFound) {
         NSInteger begin = range.location + range.length;
         NSString *index = [url substringFromIndex:begin];
         NSLog(@ "img: %@" , index);
         return   NO;
     }
     return   YES;
    
}


2、如何实现UIWebView高度自适应?

UIWebView自适应高度的方案有很多,选择一个较为科学的方式,显得尤为重要。

本方案中,通过【KVO监听Webview的contentSize】来实现,需要注意KVO的添加、移除,稍有不慎有Crash风险。

示例:

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
// 添加监听
[self.webView.scrollView addObserver:self forKeyPath:@ "contentSize"   options:NSKeyValueObservingOptionNew context:nil];
- (UIWebView *)webView
{
     if   (!_webView) {
         _webView = [[UIWebView alloc] initWithFrame:CGRectMake( 0 0 , self.frame.size.width, self.frame.size.height)];
         _webView.delegate = self;
         _webView.scrollView.bounces = NO;
         _webView.scrollView.showsHorizontalScrollIndicator = NO;
         _webView.scrollView.scrollEnabled = NO;
         _webView.scalesPageToFit = YES;
     }
     return   _webView;
}
// 修改webview的frame
- ( void )observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:( void   *)context
{
     if   ([keyPath isEqualToString:@ "contentSize" ]) {
         CGSize resize = [self.webView sizeThatFits:CGSizeZero];
         self.webView.frame =  CGRectMake( 0 0 , CGRectGetWidth(self.frame), resize.height);
     }
}
// 移除监听
-( void )dealloc
{
     [self.webView.scrollView removeObserver:self forKeyPath:@ "contentSize" ];
}

3、如何实现UIWebView显示webp格式图片?

UIWebView、WKWebview本身都不支持webp格式图片,需要额外扩展。

示例:

1
2
3
4
5
6
7
8
// 导入头文件
# import   // 注册 MagicURLProtocol
[[MagicWebViewWebPManager shareManager] registerMagicURLProtocolWebView:self.webView];
// 销毁 MagicURLProtocol
-( void )dealloc
{
     [[MagicWebViewWebPManager shareManager] unregisterMagicURLProtocolWebView:self.webView];
}

4、如何实现图文混排 + UIKit组件?

使用UIWebView加载自定义HTML代码的方式,实现图文混排。

点击图片,放大,function()跳转链接,携带selectIndex标识,通过拦截UIWebView的请求来获取selectIndex标识。

通过KVO获取到WebView高度,重新设置webView.frame,collectionView.frame,scrollView.contentSize

本方案中,图文混排+UIKit组件,具体逻辑如下:

1.png

5、如何自定义HTML代码?

本方案中,以纯图片为例,处理后的HTML如下:

1
                                     ......

6、如何实现并发执行多个网络请求,统一处理?

本方案中,利用GCD创建队列组,提交多个任务到队列组,多个任务同时执行,监听队列组执行完毕,在主线程刷新UI。

注意: dispatch_group_enter() 、 dispatch_group_leave()将队列组中的任务未执行完毕的任务数目加减1(两个函数要配合使用)

示例:

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
34
35
- ( void )exampleMoreNetwork{
      
     dispatch_group_t group = dispatch_group_create();
     dispatch_queue_t serialQueue = dispatch_queue_create( "magic_gcd_group" , DISPATCH_QUEUE_SERIAL);
      
     // 网络请求1
     dispatch_group_enter(group);
     dispatch_group_async(group, serialQueue, ^{
         [[MagicNetworkManager shareManager] GET:@ "网络请求1"   Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
             dispatch_group_leave(group);
         } Failure:^(NSURLResponse *response, id error) {
             dispatch_group_leave(group);
         }];
     });
      
     // 网络请求2
     dispatch_group_enter(group);
     dispatch_group_async(group, serialQueue, ^{
         [[MagicNetworkManager shareManager] GET:@ "网络请求2"   Parameters:nil Success:^(NSURLResponse *response, id responseObject) {
             dispatch_group_leave(group);
         } Failure:^(NSURLResponse *response, id error) {
             dispatch_group_leave(group);
         }];
     });
      
     // 所有网络请求结束
     dispatch_group_notify(group, serialQueue, ^{
         dispatch_async(dispatch_get_global_queue( 0 0 ), ^{
             dispatch_async(dispatch_get_main_queue(), ^{
                 // 主线程刷新UI
             });
         });
     });
      
}

图文混排——核心

1.png

目录结构

实现代理方法,放大图片,跳转商品,置顶。

实现针对showjoy.com域名,图片url拼接.webp。

实现UIScrollView作为根容器,自适应内容高度。

实现UIWebView支持webp格式图片。

实现自定义HTML代码,图片居中,window.location.href事件传递selectIndex,UIWebView代理拦截selectIndex。

通过HTML,JavaScript,还可以实现更多功能。。。。。。

ProductLoadMorePicTextView.h

1
2
3
4
5
6
7
8
9
10
11
# import   # import   "ProductDetailModel.h"
# import   "ProductLoadMorePicTextModel.h"
@protocol ProductLoadMorePicTextViewDelegate - ( void )productLoadMorePicTextViewZoomImageWithIndex:(NSInteger)index;
- ( void )productLoadMorePicTextViewPushProductWithSkuId:(NSString *)skuId;
- ( void )productLoadMorePicTextViewGoTop;
@end
@ interface   ProductLoadMorePicTextView : UIView
@property (nonatomic, weak) id  delegate;
- (instancetype)initWithFrame:(CGRect)frame productDetailModel:(ProductDetailModel *)productDetailModel picTextModel:(ProductLoadMorePicTextModel *)picTextModel;
- ( void )reload;
@end

ProductLoadMorePicTextView.m

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# import   "ProductLoadMorePicTextView.h"
# import   "ProductLoadMorePicTextCollectionViewCell.h"
# import   "MagicScrollPageRefreshHeader.h"
# import   static   const   CGFloat recommendViewHeight =  170.0 ;
static   const   CGFloat recommendViewSpace =  10.0 ;
static   const   CGFloat recommendItemWidth =  105.0 ;
static   const   CGFloat recommendItemSpace =  5.0 ;
static   const   CGFloat recommendTitleHeight =  40.0 ;
@ interface   ProductLoadMorePicTextView ()@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UILabel *recommendLabel;
@property (nonatomic, strong) NSMutableArray *recommendDataArray;
@property (nonatomic, strong) NSMutableArray *picTextDataArray;
@end
@implementation ProductLoadMorePicTextView
- (instancetype)initWithFrame:(CGRect)frame productDetailModel:(ProductDetailModel *)productDetailModel picTextModel:(ProductLoadMorePicTextModel *)picTextModel
{
     self = [ super   initWithFrame:frame];
     if   (self) {
         self.recommendDataArray = [NSMutableArray arrayWithArray:productDetailModel.recommend];
         self.picTextDataArray = [NSMutableArray arrayWithArray:picTextModel.itemPic.packageImages];
         [self createSubViewsWithPicTextModel:picTextModel];
     }
     return   self;
}
- ( void )createSubViewsWithPicTextModel:(ProductLoadMorePicTextModel *)picTextModel
{
     [self addSubview:self.scrollView];
     [[MagicWebViewWebPManager shareManager] registerMagicURLProtocolWebView:self.webView];
     [self.scrollView addSubview:self.webView];
     [self.scrollView addSubview:self.recommendLabel];
     [self.scrollView addSubview:self.collectionView];
     [self.webView.scrollView addObserver:self forKeyPath:@ "contentSize"   options:NSKeyValueObservingOptionNew context:nil];
      
     MC_SELF_WEAK(self)
     MagicScrollPageRefreshHeader *header = [MagicScrollPageRefreshHeader headerWithRefreshingBlock:^{
         [weakself.scrollView.mj_header endRefreshing];
         [weakself executeProductLoadMorePicTextViewGoTop];
     }];
     self.scrollView.mj_header = header;
}
#pragma mark -Lazy
- (UIScrollView *)scrollView
{
     if   (!_scrollView) {
         _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake( 0 0 , self.frame.size.width, self.frame.size.height)];
         _scrollView.backgroundColor = [UIColor colorWithRed: 0.95   green: 0.95   blue: 0.95   alpha: 1.00 ];
     }
     return   _scrollView;
}
- (UIWebView *)webView
{
     if   (!_webView) {
         _webView = [[UIWebView alloc] initWithFrame:CGRectMake( 0 0 , self.frame.size.width, self.frame.size.height)];
         _webView.delegate = self;
         _webView.scrollView.bounces = NO;
         _webView.scrollView.showsHorizontalScrollIndicator = NO;
         _webView.scrollView.scrollEnabled = NO;
         _webView.scalesPageToFit = YES;
     }
     return   _webView;
}
- (UILabel *)recommendLabel{
     if   (!_recommendLabel) {
         _recommendLabel = [[UILabel alloc] init];
         _recommendLabel.text = @ "   更多推荐" ;
         _recommendLabel.textColor = [UIColor colorWithRed: 0.30   green: 0.30   blue: 0.30   alpha: 1.00 ];
         _recommendLabel.font = [UIFont systemFontOfSize: 12 ];
         _recommendLabel.backgroundColor = [UIColor whiteColor];
     }
     return   _recommendLabel;
}
- (UICollectionView *)collectionView
{
     if   (!_collectionView) {
         UICollectionViewFlowLayout *flowLayout = [UICollectionViewFlowLayout  new ];
         flowLayout.sectionInset = UIEdgeInsetsMake( 0 0 0 , recommendItemSpace);
         flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
         flowLayout.itemSize = CGSizeMake(recommendItemWidth, recommendViewHeight);
         _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:flowLayout];
         _collectionView.backgroundColor = [UIColor whiteColor];
         _collectionView.delegate = self;
         _collectionView.dataSource = self;
         [_collectionView registerClass:[ProductLoadMorePicTextCollectionViewCell  class ] forCellWithReuseIdentifier:@ "cell" ];
     }
     return   _collectionView;
}
- ( void )observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:( void   *)context
{
     if   ([keyPath isEqualToString:@ "contentSize" ]) {
         CGSize resize = [self.webView sizeThatFits:CGSizeZero];
         self.webView.frame =  CGRectMake( 0 0 , CGRectGetWidth(self.frame), resize.height);
         self.recommendLabel.frame = CGRectMake( 0 , CGRectGetMaxY(self.webView.frame) + recommendViewSpace, CGRectGetWidth(self.frame), recommendTitleHeight);
         self.collectionView.frame = CGRectMake( 0 , CGRectGetMaxY(self.recommendLabel.frame), CGRectGetWidth(self.frame), recommendViewHeight);
         self.scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.frame), CGRectGetMaxY(self.collectionView.frame) + recommendViewSpace);
     }
}
-( void )dealloc
{
     [[MagicWebViewWebPManager shareManager] unregisterMagicURLProtocolWebView:self.webView];
     [self.webView.scrollView removeObserver:self forKeyPath:@ "contentSize" ];
     self.scrollView = nil;
     self.webView = nil;
     self.collectionView = nil;
     self.recommendDataArray = nil;
     self.picTextDataArray = nil;
}
#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
     return   [self handleWebviewEventWithRequest:request];
}
- ( void )webViewDidStartLoad:(UIWebView *)webView
{
      
}
- ( void )webViewDidFinishLoad:(UIWebView *)webView
{
      
}
- ( void )webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
     NSLog(@ "商品详情web错误 %@" , error);
}
- (BOOL)handleWebviewEventWithRequest:(NSURLRequest *)request
{
     NSString *url = request.URL.absoluteString;
     NSRange range = [url rangeOfString:@ "selectIndex=" ];
     if   (range.location != NSNotFound) {
         NSInteger begin = range.location + range.length;
         NSString *index = [url substringFromIndex:begin];
         [self executeProductLoadMorePicTextViewZoomImageWithIndexString:index];
         return   NO;
     }
     return   YES;
}
#pragma mark - CustomHTML
- ( void )loadWebViewCustomHTMLWithImageUrls:(NSArray *)imageUrls
{
     NSMutableString *html = [NSMutableString string];
     [html appendString:@ "" ];
     [html appendString:@ "" ];
     [html appendString:@ "" ];
     [html appendString:@ "" ];
     [html appendString:[self settingWebViewBodyWithImageUrlArray:imageUrls]];
     [html appendString:@ "" ];
     [html appendString:@ "" ];
     [self.webView loadHTMLString:html baseURL:nil];
}
- (NSString *)settingWebViewBodyWithImageUrlArray:(NSArray *)imageUrlArray
{
     NSMutableString *body = [NSMutableString string];
     for   (NSInteger i =  0 ; i < imageUrlArray.count; i++) {
         NSString *imgUrl = [NSString stringWithFormat:@ "%@" , [imageUrlArray objectAtIndex:i]];
         imgUrl = [self handlerImgUrlString:imgUrl];
         NSMutableString *html = [NSMutableString string];
         [html appendString:@ "" ];
         NSString *onload = [NSString stringWithFormat:@ "this.onclick = function() {window.location.href = 'selectIndex=' + %ld;}" , i];
         [html appendFormat:@ "" , onload, imgUrl];
         [html appendString:@ "" ];
         [body appendString:html];
     }
     return   body;
}
#pragma mark -UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
     return   self.recommendDataArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
     ProductLoadMorePicTextCollectionViewCell *cell = (ProductLoadMorePicTextCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@ "cell"   forIndexPath:indexPath];
     cell.productRecommendModel = [self.recommendDataArray objectAtIndex:indexPath.row];
     return   cell;
}
#pragma mark -UICollectionViewDelegate
- ( void )collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    ProductRecommend *productRecommendModel = [self.recommendDataArray objectAtIndex:indexPath.row];
    [self executeProductLoadMorePicTextViewPushProductWithSkuId:productRecommendModel.ID];
}
#pragma mark -ProductLoadMoreViewDelegate
- ( void )executeProductLoadMorePicTextViewZoomImageWithIndexString:(NSString *)indexString
{
     if   ([self.delegate respondsToSelector:@selector(productLoadMorePicTextViewZoomImageWithIndex:)]) {
         [self.delegate productLoadMorePicTextViewZoomImageWithIndex:[indexString integerValue]];
     }
}
- ( void )executeProductLoadMorePicTextViewPushProductWithSkuId:(NSInteger)skuId
{
     if   ([self.delegate respondsToSelector:@selector(productLoadMorePicTextViewPushProductWithSkuId:)]) {
         [self.delegate productLoadMorePicTextViewPushProductWithSkuId:[NSString stringWithFormat:@ "%ld" , skuId]];
     }
}
- ( void )executeProductLoadMorePicTextViewGoTop
{
     if   ([self.delegate respondsToSelector:@selector(productLoadMorePicTextViewGoTop)]) {
         [self.delegate productLoadMorePicTextViewGoTop];
     }
}
#pragma mark - Reload
- ( void )reload{
     [self loadWebViewCustomHTMLWithImageUrls:self.picTextDataArray];
     [self.collectionView reloadData];
}
#pragma mark - IMGURL
- (NSString *)handlerImgUrlString:(NSString *)imgUrlString
{
     NSString *result = [NetworkManager httpsSchemeHandler:imgUrlString];
     // webp
     if   ([result containsString:@ "showjoy.com" ] && ![result hasSuffix:@ ".webp" ]) {
         result = [result stringByAppendingString:@ ".webp" ];
     }
     return   result;
}
@end

图文混排——使用

ProductLoadMoreViewController中,保证两个接口都请求完成后,刷新ProductLoadMorePicTextView。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# import   "ProductLoadMoreViewController.h"
# import   "MagicNetworkManager.h"
# import   "ProductLoadMorePicTextView.h"
static   NSString *  const   SJProductAPI = @ "https://shopappserver.showjoy.com/api/shop/sku" ;
static   NSString *  const   SJProductPicTextAPI = @ "https://shopappserver.showjoy.com/api/shop/item/pictext" ;
static   NSString *  const   SJProductSkuId = @ "146931" ;
@ interface   ProductLoadMoreViewController () @end
@implementation ProductLoadMoreViewController{
     ProductDetailModel *_productModel;
     ProductLoadMorePicTextModel *_productPicTextModel;
     ProductLoadMorePicTextView *_picTextView;
}
- ( void )viewDidLoad {
     [ super   viewDidLoad];
     // Do any additional setup after loading the view.
     self.view.backgroundColor = [UIColor grayColor];
     [self networkRequestData];
}
#pragma mark - Network
- ( void )networkRequestData
{
    [QuicklyHUD showWindowsProgressHUDText:@ "加载中..." ];
     dispatch_group_t group = dispatch_group_create();
     dispatch_queue_t serialQueue = dispatch_queue_create( "product_group" , DISPATCH_QUEUE_SERIAL);
      
     // 商品信息
     dispatch_group_enter(group);
     dispatch_group_async(group, serialQueue, ^{
         [[MagicNetworkManager shareManager] GET:SJProductAPI Parameters:@{@ "skuId"   : SJProductSkuId} Success:^(NSURLResponse *response, id responseObject) {
             [ProductDetailModel mj_setupObjectClassInArray:^NSDictionary *{
                 return   @{@ "shop"   : [ProductShop  class ],
                          @ "skuList"   : [ProductSkuList  class ],
                          @ "value"   : [ProductValue  class ],
                          @ "saleInfo"   : [ProductSaleInfo  class ],
                          @ "recommend"   : [ProductRecommend  class ],
                          @ "skuCommission"   : [ProductSkuCommission  class ],
                          @ "item"   : [ProductItem  class ],
                          @ "tagSkus"   : [ProductTagSkus  class ],
                          @ "tagMap"   : [ProductTagMap  class ],
                          @ "skuEnsures"   : [ProductSkuEnsures  class ],
                          @ "salesPromotion"   : [ProductSalesPromotion  class ]};
             }];
             _productModel = [ProductDetailModel mj_objectWithKeyValues:[responseObject valueForKey:@ "data" ]];
             dispatch_group_leave(group);
         } Failure:^(NSURLResponse *response, id error) {
             dispatch_group_leave(group);
         }];
     });
      
     // 图文信息
     dispatch_group_enter(group);
     dispatch_group_async(group, serialQueue, ^{
         [[MagicNetworkManager shareManager] GET:SJProductPicTextAPI Parameters:@{@ "skuId"   : SJProductSkuId} Success:^(NSURLResponse *response, id responseObject) {
             [ProductLoadMorePicTextModel mj_setupObjectClassInArray:^NSDictionary *{
                 return   @{@ "item"   : [PicTextItem  class ],
                          @ "itemPic"   : [PicTextItemPic  class ],
                          @ "spu"   : [PicTextSpu  class ]};
             }];
             _productPicTextModel = [ProductLoadMorePicTextModel mj_objectWithKeyValues:[responseObject valueForKey:@ "data" ]];
             dispatch_group_leave(group);
         } Failure:^(NSURLResponse *response, id error) {
             dispatch_group_leave(group);
         }];
     });
      
     // 主线程刷新UI
     dispatch_group_notify(group, serialQueue, ^{
         dispatch_async(dispatch_get_global_queue( 0 0 ), ^{
             dispatch_async(dispatch_get_main_queue(), ^{
                 [QuicklyHUD hiddenMBProgressHUDForView:MC_APP_WINDOW];
                 [self reloadPicTextView];
             });
         });
     }); 
}
#pragma mark - Reload
- ( void )reloadPicTextView
{
     if   (_picTextView) {
         [_picTextView removeFromSuperview];
         _picTextView.delegate = nil;
         _picTextView = nil;
     }
     CGFloat border =  20 .0f;
     _picTextView = [[ProductLoadMorePicTextView alloc] initWithFrame:CGRectMake(border, border, MC_SCREEN_W -  2   * border, MC_SCREEN_H - MC_NAVIGATION_BAR_H - MC_STATUS_BAR_H -  2   * border) productDetailModel:_productModel picTextModel:_productPicTextModel];
     _picTextView.delegate = self;
     [self.view addSubview:_picTextView];
     [_picTextView reload];
}
#pragma mark - ProductLoadMorePicTextViewDelegate
- ( void )productLoadMorePicTextViewGoTop
{
     [QuicklyHUD showWindowsOnlyTextHUDText:@ "Go Top" ];
}
- ( void )productLoadMorePicTextViewZoomImageWithIndex:(NSInteger)index
{
     [QuicklyHUD showWindowsOnlyTextHUDText:[NSString stringWithFormat:@ "img: %ld" , index]];
}
- ( void )productLoadMorePicTextViewPushProductWithSkuId:(NSString *)skuId
{
     [QuicklyHUD showWindowsOnlyTextHUDText:[NSString stringWithFormat:@ "skuId: %@" , skuId]];
}
@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值