blob: 02d7f5c44d5cb3fe61515ff7b2f28d166ff41de4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#import "WXRefreshComponent.h"
#import "WXScrollerComponent.h"
#import "WXLoadingIndicator.h"
#import "WXComponent_internal.h"
#import "WXLog.h"
#import "WXComponent+Layout.h"
@interface WXRefreshComponent()
{
NSTimeInterval _refreshStateTriggerTime;
}
@property (nonatomic) BOOL displayState;
@property (nonatomic) BOOL initFinished;
@property (nonatomic) BOOL refreshEvent;
@property (nonatomic) BOOL pullingdownEvent;
@property (nonatomic, weak) WXLoadingIndicator *indicator;
@end
@implementation WXRefreshComponent
- (instancetype)initWithRef:(NSString *)ref type:(NSString *)type styles:(NSDictionary *)styles attributes:(NSDictionary *)attributes events:(NSArray *)events weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
_refreshEvent = NO;
_pullingdownEvent = NO;
if (attributes[@"display"]) {
if ([attributes[@"display"] isEqualToString:@"show"]) {
_displayState = YES;
} else if ([attributes[@"display"] isEqualToString:@"hide"]) {
_displayState = NO;
} else {
WXLogError(@"");
}
}
self.flexCssNode->setStylePositionType(WeexCore::kAbsolute);
}
return self;
}
- (void)viewDidLoad
{
_initFinished = YES;
if (!_displayState) {
[_indicator.view setHidden:YES];
}
}
- (void)layoutDidFinish
{
[self.view setFrame: (CGRect) {
.size = self.calculatedFrame.size,
.origin.x = self.calculatedFrame.origin.x,
.origin.y = self.view.frame.origin.y - CGRectGetHeight(self.calculatedFrame)
}];
}
- (void)viewWillUnload
{
_displayState = NO;
_refreshEvent = NO;
_initFinished = NO;
}
- (void)refresh
{
if (!_refreshEvent || _displayState) {
return;
}
#ifdef DEBUG
WXLogDebug(@"flexLayout -> refreshComponent : refresh ref:%@",self.ref);
#endif
[self fireEvent:@"refresh" params:nil];
}
- (void)pullingdown:(NSDictionary*)param
{
if (!_pullingdownEvent) {
return ;
}
#ifdef DEBUG
WXLogDebug(@"flexLayout -> refreshComponent : pullingdown ,ref:%@",self.ref);
#endif
[self fireEvent:@"pullingdown" params:param];
}
- (BOOL)_insertSubcomponent:(WXComponent *)subcomponent atIndex:(NSInteger)index
{
BOOL inserted = [super _insertSubcomponent:subcomponent atIndex:index];
if ([subcomponent isKindOfClass:[WXLoadingIndicator class]]) {
_indicator = (WXLoadingIndicator*)subcomponent;
}
return inserted;
}
- (void)updateAttributes:(NSDictionary *)attributes
{
if (attributes[@"display"]) {
if ([attributes[@"display"] isEqualToString:@"show"]) {
_displayState = YES;
} else if ([attributes[@"display"] isEqualToString:@"hide"]) {
_displayState = NO;
} else {
WXLogError(@"");
}
[self setDisplay];
}
}
- (void)addEvent:(NSString *)eventName
{
if ([eventName isEqualToString:@"refresh"]) {
_refreshEvent = YES;
}
if ([eventName isEqualToString:@"pullingdown"]) {
_pullingdownEvent = YES;
}
}
- (void)removeEvent:(NSString *)evetName
{
if ([evetName isEqualToString:@"refresh"]) {
_refreshEvent = NO;
}
if ([evetName isEqualToString:@"pullingdown"]) {
_pullingdownEvent = NO;
}
}
- (void)setDisplay
{
id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
if (scrollerProtocol == nil || !_initFinished)
return;
if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
[[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
UIEdgeInsets inset = [scrollerProtocol contentInset];
if (_displayState) {
inset.top = self.calculatedFrame.size.height;
if ([_indicator.view isHidden]) {
[_indicator.view setHidden:NO];
}
[_indicator start];
} else {
inset.top = 0;
[_indicator stop];
}
[scrollerProtocol setContentInset:inset];
} else {
CGPoint offset = [scrollerProtocol contentOffset];
if (_displayState) {
offset.y = -self.calculatedFrame.size.height;
if ([_indicator.view isHidden]) {
[_indicator.view setHidden:NO];
}
[_indicator start];
[scrollerProtocol setContentOffset:offset animated:YES];
_refreshStateTriggerTime = CFAbsoluteTimeGetCurrent();
} else {
offset.y = 0;
[_indicator stop];
if (CFAbsoluteTimeGetCurrent() - _refreshStateTriggerTime < 0.3) {
/* If javascript doesn't do any refreshing and only update 'display' attribute very quickly.
The previous '[scrollerProtocol setContentOffset:offset animated:YES];' is not finished,
we should also use '[scrollerProtocol setContentOffset:offset animated:YES]' to restore offset.
Or the scroller will not stop at 0.
*/
[scrollerProtocol setContentOffset:offset animated:YES];
}
else {
[UIView animateWithDuration:0.25 animations:^{
[scrollerProtocol setContentOffset:offset];
}];
}
}
/* If we are adding elements while refreshing, like this demo:http://dotwe.org/vue/f541ed72a121db8447a233b777003e8a
the scroller cannot stay at (0, 0) when all animations are finished.
So we use
[scrollerProtocol setContentOffset:offset animated:YES];
when _displayState is TRUE and use
[UIView animateWithDuration:0.25 animations:^{
[scrollerProtocol setContentOffset:offset];
}];
when _displayState is FALSE.
All things go well. Probably setContentOffset: has higher priority than setContentOffset:animated:
*/
}
}
- (BOOL)displayState
{
return _displayState;
}
- (void)setIndicatorHidden:(BOOL)hidden {
[_indicator.view setHidden:hidden];
id<WXScrollerProtocol> scrollerProtocol = self.ancestorScroller;
if (scrollerProtocol == nil || !_initFinished)
return;
if ([scrollerProtocol respondsToSelector:@selector(refreshType)] &&
[[scrollerProtocol refreshType] isEqualToString:@"refreshForAppear"]) {
UIEdgeInsets inset = [scrollerProtocol contentInset];
if (!hidden) {
inset.top = self.calculatedFrame.size.height;
[_indicator start];
} else {
inset.top = 0;
[_indicator stop];
}
[scrollerProtocol setContentInset:inset];
}
}
@end