问题汇总
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组件,具体逻辑如下:
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
});
});
});
}
|
图文混排——核心
目录结构
实现代理方法,放大图片,跳转商品,置顶。
实现针对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
|