登录 立即注册
金钱:

Code4App-iOS开发-iOS 开源代码库-iOS代码实例搜索-iOS特效示例-iOS代码例子下载-Code4App.com

iOS中UIScrollView、UIWebView、UICollectionView实现商品详情页图文混排 ... [复制链接]

2018-8-2 10:25
sergiochanTest 阅读:551 评论:0 赞:0
Tag:  

实现思路

  1. 将文本和图片拼接为HTML代码。

  2. 使用JavaScript添加点击事件。

  3. 使用MagicWebViewWebP提供UIWebView加载webp格式图片支持。

  4. 使用UIWebView加载HTML代码。

  5. 使用UIWebView代理方法,拦截页面发出的请求,获取selectIndex。

实现效果

组件描述说明
UIScrollView根容器高度自适应(KVO处理UIWebView + UICollectionView高度)
UIWebView图文混排展示加载HTML代码
UICollectionView更多推荐展示

123.gif

实现效果

问题汇总

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(00, 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(00, CGRectGetWidth(self.frame), resize.height);
    }
}
// 移除监听
-(void)dealloc
{
    [self.webView.scrollView removeObserver:self forKeyPath:@"contentSize"];
}

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

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

可以直接访问我的GitHub,下载MagicWebViewWebP,将【MagicWebViewWebP.framework】直接导入工程。

参考: UIWebView、WKWebView支持WebP图片显示

示例:

1
2
3
4
5
6
7
8
9
// 导入头文件
#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(两个函数要配合使用)

参考: 玩转GCD

示例:

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(00), ^{
            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
12
13
#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) iddelegate;
- (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
214
215
216
#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(00, 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(00, 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(000, 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(00, 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