ComponentTextKit/CKTextComponentView.mm (101 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import "CKTextComponentView.h"
#import "CKTextComponentViewInternal.h"
#import <RenderCore/RCAssert.h>
#import <ComponentTextKit/CKAsyncLayer.h>
#import <ComponentTextKit/CKAsyncLayerSubclass.h>
#import <ComponentTextKit/CKTextKitRenderer.h>
#import <ComponentTextKit/CKTextKitRendererCache.h>
#import <ComponentKit/CKInternalHelpers.h>
#import "CKTextComponentLayer.h"
#import "CKTextComponentLayerHighlighter.h"
#import "CKTextComponentViewControlTracker.h"
@implementation CKTextComponentView
{
CKTextComponentViewControlTracker *_controlTracker;
}
+ (Class)layerClass
{
return [CKTextComponentLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
// Set some sensible defaults for a text view
self.contentScaleFactor = CKScreenScale();
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
if (![self.backgroundColor isEqual:backgroundColor]) {
BOOL opaque = self.textLayer.opaque;
[super setBackgroundColor:backgroundColor];
// for reasons I don't understand, UIView is setting opaque=NO on self.layer when setting the background color. we
// don't want to force our rich text layers to draw with blending, so check if we can keep the opacity value after
// setting the backgroundColor.
if (opaque) {
CGFloat alpha = 0.0;
if ([backgroundColor getRed:NULL green:NULL blue:NULL alpha:&alpha] ||
[backgroundColor getWhite:NULL alpha:&alpha] ||
[backgroundColor getHue:NULL saturation:NULL brightness:NULL alpha:&alpha]) {
if (alpha == 1.0) {
self.textLayer.opaque = YES;
[self.textLayer setNeedsDisplay];
}
}
}
}
}
- (CKTextComponentLayer *)textLayer
{
return (CKTextComponentLayer *)self.layer;
}
- (void)setRenderer:(CKTextKitRenderer *)renderer
{
[self.textLayer setRenderer:renderer];
}
- (CKTextKitRenderer *)renderer
{
return [self.textLayer renderer];
}
#pragma mark - Control Tracking
- (CKTextComponentViewControlTracker *)controlTracker
{
if (!_controlTracker) {
// Lazily generate a control tracker to receive UIControl touch input.
_controlTracker = [[CKTextComponentViewControlTracker alloc] init];
}
return _controlTracker;
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
if (![super beginTrackingWithTouch:touch withEvent:event]) {
return NO;
}
return [self.controlTracker beginTrackingForTextComponentView:self withTouch:touch withEvent:event];
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
if (![super continueTrackingWithTouch:touch withEvent:event]) {
return NO;
}
return [self.controlTracker continueTrackingForTextComponentView:self withTouch:touch withEvent:event];
}
- (void)cancelTrackingWithEvent:(UIEvent *)event
{
[self.controlTracker cancelTrackingForTextComponentView:self withEvent:event];
[super cancelTrackingWithEvent:event];
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
[self.controlTracker endTrackingForTextComponentView:self withTouch:touch withEvent:event];
[super endTrackingWithTouch:touch withEvent:event];
}
#pragma mark - Touch Interaction
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer
{
// We want the same behavior as UIButton: "...a UIButton, by default, does return NO for a single tap
// UITapGestureRecognizer whose view is not the UIButton itself."
// http://www.apeth.com/iOSBook/ch18.html#_gesture_recognizers
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)recognizer;
if (tapRecognizer.numberOfTapsRequired == 1 && tapRecognizer.view != self) {
return NO;
}
}
return [super gestureRecognizerShouldBegin:recognizer];
}
@end