diff --git a/CHANGELOG.md b/CHANGELOG.md index e159a77..03fa7b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. --- +## [2.1.0](https://github.com/laptobbe/TSMarkdownParser/releases/tag/2.1.0) (2016/05/01) +Released on 2016/05/01. All issues associated with this milestone can be found using this [filter](https://github.com/laptobbe/TSMarkdownParser/issues?q=milestone%3A2.1.0+is%3Aclosed). + +#### Added +* Added custom font for quote text for a better user experience [#47](https://github.com/laptobbe/TSMarkdownParser/pull/47) +* Added support to skip NSLinkAttributeName for UILabel customization. [#45](https://github.com/laptobbe/TSMarkdownParser/issues/45) +* Added partial support for remote image URL. [#16](https://github.com/laptobbe/TSMarkdownParser/issues/16) +* Added HeaderDoc support + + ## [2.0.3](https://github.com/laptobbe/TSMarkdownParser/releases/tag/2.0.3) (2016/04/09) Released on 2016/04/09. All issues associated with this milestone can be found using this [filter](https://github.com/laptobbe/TSMarkdownParser/issues?q=milestone%3A2.0.3+is%3Aclosed). @@ -13,6 +23,7 @@ Released on 2016/04/09. All issues associated with this milestone can be found u #### Fixed * Fixed CodeEscaping [#46](https://github.com/laptobbe/TSMarkdownParser/issues/46) + ## [2.0.2](https://github.com/laptobbe/TSMarkdownParser/releases/tag/2.0.2) (2016/03/29) Released on 2016/03/29. All issues associated with this milestone can be found using this [filter](https://github.com/laptobbe/TSMarkdownParser/issues?q=milestone%3A2.0.2+is%3Aclosed). diff --git a/TSMarkdownParser.podspec b/TSMarkdownParser.podspec index 2901ae5..8ffe333 100644 --- a/TSMarkdownParser.podspec +++ b/TSMarkdownParser.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "TSMarkdownParser" - s.version = "2.0.3" + s.version = "2.1.0" s.summary = "A markdown to NSAttributedString parser for iOS and OSX" s.description = <<-DESC diff --git a/TSMarkdownParser.xcodeproj/project.pbxproj b/TSMarkdownParser.xcodeproj/project.pbxproj index e81b709..fae5928 100644 --- a/TSMarkdownParser.xcodeproj/project.pbxproj +++ b/TSMarkdownParser.xcodeproj/project.pbxproj @@ -1094,6 +1094,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_TESTABILITY = YES; @@ -1109,7 +1110,10 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 6.0; ONLY_ACTIVE_ARCH = YES; @@ -1147,6 +1151,7 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; @@ -1155,7 +1160,10 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 6.0; PRODUCT_BUNDLE_IDENTIFIER = "se.computertalk.$(PRODUCT_NAME:rfc1034identifier)"; diff --git a/TSMarkdownParser/TSBaseParser.h b/TSMarkdownParser/TSBaseParser.h index f130ef1..665fc83 100644 --- a/TSMarkdownParser/TSBaseParser.h +++ b/TSMarkdownParser/TSBaseParser.h @@ -14,23 +14,21 @@ typedef void (^TSMarkdownParserMatchBlock)(NSTextCheckingResult *match, NSMutabl @interface TSBaseParser : NSObject -/* +/** Default attributes for `attributedStringFromMarkdown:`. */ @property (nonatomic, strong, nullable) NSDictionary *defaultAttributes; -/* Applies defaultAttributes then markdown */ +/// Applies defaultAttributes then markdown - (NSAttributedString *)attributedStringFromMarkdown:(NSString *)markdown; -/* Applies attributes then markdown */ +/// Applies attributes then markdown - (NSAttributedString *)attributedStringFromMarkdown:(NSString *)markdown attributes:(nullable NSDictionary *)attributes; -/* Applies markdown */ +/// Applies markdown - (NSAttributedString *)attributedStringFromAttributedMarkdownString:(NSAttributedString *)attributedString; -/* - Adds a custom parsing rule to parser. Use `[TSMarkdownParser new]` for an empty parser. - */ +/// Adds a custom parsing rule to parser. Use `[TSMarkdownParser new]` for an empty parser. - (void)addParsingRuleWithRegularExpression:(NSRegularExpression *)regularExpression block:(TSMarkdownParserMatchBlock)block; @end diff --git a/TSMarkdownParser/TSBaseParser.m b/TSMarkdownParser/TSBaseParser.m index d140c68..603ea3f 100644 --- a/TSMarkdownParser/TSBaseParser.m +++ b/TSMarkdownParser/TSBaseParser.m @@ -80,7 +80,8 @@ - (NSAttributedString *)attributedStringFromMarkdown:(NSString *)markdown attrib - (NSAttributedString *)attributedStringFromAttributedMarkdownString:(NSAttributedString *)attributedString { NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString]; - + // TODO: evaluate performances of `beginEditing`/`endEditing` + //[mutableAttributedString beginEditing]; @synchronized (self) { for (TSExpressionBlockPair *expressionBlockPair in self.parsingPairs) { NSTextCheckingResult *match; @@ -93,6 +94,7 @@ - (NSAttributedString *)attributedStringFromAttributedMarkdownString:(NSAttribut } } } + //[mutableAttributedString endEditing]; return mutableAttributedString; } diff --git a/TSMarkdownParser/TSMarkdownParser.h b/TSMarkdownParser/TSMarkdownParser.h index 6e0d370..f1ffbad 100644 --- a/TSMarkdownParser/TSMarkdownParser.h +++ b/TSMarkdownParser/TSMarkdownParser.h @@ -12,6 +12,7 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^TSMarkdownParserFormattingBlock)(NSMutableAttributedString *attributedString, NSRange range); typedef void (^TSMarkdownParserLevelFormattingBlock)(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level); +typedef void (^TSMarkdownParserLinkFormattingBlock)(NSMutableAttributedString *attributedString, NSRange range, NSString * _Nullable link); @interface TSMarkdownParser : TSBaseParser @@ -26,8 +27,24 @@ typedef void (^TSMarkdownParserLevelFormattingBlock)(NSMutableAttributedString * @property (nonatomic, strong) NSDictionary *monospaceAttributes; @property (nonatomic, strong) NSDictionary *strongAttributes; @property (nonatomic, strong) NSDictionary *emphasisAttributes; +/** + * standardParser setting for NSLinkAttributeName + * + * When YES, references to URL are lost and you have freedom to customize the appearance of text. + * + * When NO, reference to URL are kept with NSLinkAttributeName and restrictions to customize the appearance of links apply: + * + * * UILabel is forcing all links to be displayed blue and underline, links aren't clickable + * + * * UITextView's tintColor property is controlling all links color, links are clickable + * + * * NSTextView's linkTextAttributes property is controlling all links attributes + * + * If you want clickable links with an UILabel subclass, you should leave skipLinkAttribute to NO and consider using KILabel, TTTAttributedLabel, FRHyperLabel, ... As a bonus, all will lift off the UILabel appearance restrictions for links. + */ +@property (nonatomic, assign) BOOL skipLinkAttribute; -/* +/** Provides the following default parsing rules from below examples: * Escaping parsing * Code escaping parsing using monospaceAttributes @@ -49,62 +66,72 @@ typedef void (^TSMarkdownParserLevelFormattingBlock)(NSMutableAttributedString * /* 1. examples escaping parsing */ -// accepts "`code`", "``code``", ...; ALWAYS use together with `addCodeUnescapingParsingWithFormattingBlock:` +/// accepts "`code`", "``code``", ...; ALWAYS use together with `addCodeUnescapingParsingWithFormattingBlock:` - (void)addCodeEscapingParsing; -// accepts "\."; ALWAYS use together with `addUnescapingParsing` +/// accepts "\."; ALWAYS use together with `addUnescapingParsing` - (void)addEscapingParsing; /* 2. examples regular block parsing: headers, lists and quotes */ -// accepts "# text", "## text", ... +/// accepts "# text", "## text", ... - (void)addHeaderParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; -// accepts "* text", "+ text", "- text", "** text", "++ text", "-- text", ... +/// accepts "* text", "+ text", "- text", "** text", "++ text", "-- text", ... - (void)addListParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; -// accepts "> text", ">> text", ... +/// accepts "> text", ">> text", ... - (void)addQuoteParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; /* 3. examples short block parsing: headers and lists */ /* they are discouraged and not used by standardParser */ -// accepts "#text", "##text", ... -// (conflicts with inline parsing) +/// accepts "#text", "##text", ... +/// (conflicts with inline parsing) - (void)addShortHeaderParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; -// accepts "*text", "+text", "-text", "** text", "++ text", "-- text", ... -// (conflicts with inline parsing) +/// accepts "*text", "+text", "-text", "** text", "++ text", "-- text", ... +/// (conflicts with inline parsing) - (void)addShortListParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; -// accepts ">text", ">>text", ... -// (conflicts with inline parsing) +/// accepts ">text", ">>text", ... +/// (conflicts with inline parsing) - (void)addShortQuoteParsingWithMaxLevel:(unsigned int)maxLevel leadFormattingBlock:(TSMarkdownParserLevelFormattingBlock)leadFormattingBlock textFormattingBlock:(nullable TSMarkdownParserLevelFormattingBlock)formattingBlock; /* 4. examples inline bracket parsing: images and links */ /* text accepts newlines and non-bracket parsing */ -// accepts "![text](image)" -- (void)addImageParsingWithImageFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock alternativeTextFormattingBlock:(TSMarkdownParserFormattingBlock)alternativeFormattingBlock; -// accepts "[text](link)" -- (void)addLinkParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; +/// accepts "![text](image)" +- (void)addImageParsingWithImageFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock alternativeTextFormattingBlock:(TSMarkdownParserFormattingBlock)alternativeFormattingBlock __attribute__((deprecated("use addImageParsingWithLinkFormattingBlock: instead"))); +/// accepts "[text](link)" +- (void)addLinkParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock __attribute__((deprecated("use addLinkParsingWithLinkFormattingBlock: instead"))); +/// accepts "![text](image)" +/// @note you can use formattingBlock to asynchronously download an image from link and replace range with an NSTextAttachment. Be careful of the range when the attributedString is altered. +- (void)addImageParsingWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock; +/// accepts "[text](link)" +/// @note you can use formattingBlock to add NSLinkAttributeName +- (void)addLinkParsingWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock; /* 5. example autodetection parsing: links */ -// adds links autodetection support to parser -- (void)addLinkDetectionWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; +/// adds links autodetection support to parser +- (void)addLinkDetectionWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock __attribute__((deprecated("use addLinkDetectionWithLinkFormattingBlock: instead"))); +/// adds links autodetection support to parser +/// @note you can use formattingBlock to add NSLinkAttributeName +- (void)addLinkDetectionWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock; /* 6. examples inline parsing: monospaced, strong, emphasis and link detection */ /* text accepts newlines */ -// accepts "`text`", "``text``", ... (conflicts with `addCodeEscapingParsing`) +/// accepts "`text`", "``text``", ... +/// (conflicts with `addCodeEscapingParsing`) - (void)addMonospacedParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; -// accepts "**text**", "__text__" +/// accepts "**text**", "__text__" - (void)addStrongParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; -// accepts "*text*", "_text_" +/// accepts "*text*", "_text_" - (void)addEmphasisParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; /* 7. examples unescaping parsing */ /* to use together with `addEscapingParsing` or `addCodeEscapingParsing` */ -// accepts "`hexa`", "``hexa``", ...; to use with `addCodeEscapingParsing` +/// accepts "`hexa`", "``hexa``", ...; to use with `addCodeEscapingParsing` - (void)addCodeUnescapingParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock; -// accepts "\hexa"; to use with `addEscapingParsing` +/// accepts "\hexa"; to use with `addEscapingParsing` - (void)addUnescapingParsing; @end diff --git a/TSMarkdownParser/TSMarkdownParser.m b/TSMarkdownParser/TSMarkdownParser.m index d6a91df..45dda0c 100644 --- a/TSMarkdownParser/TSMarkdownParser.m +++ b/TSMarkdownParser/TSMarkdownParser.m @@ -64,7 +64,7 @@ + (instancetype)standardParser { /* block parsing */ - [defaultParser addHeaderParsingWithMaxLevel:0 leadFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) { + [defaultParser addHeaderParsingWithMaxLevel:0 leadFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, __unused NSUInteger level) { [attributedString deleteCharactersInRange:range]; } textFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSUInteger level) { [TSMarkdownParser addAttributes:weakParser.headerAttributes atIndex:level - 1 toString:attributedString range:range]; @@ -91,19 +91,49 @@ + (instancetype)standardParser { /* bracket parsing */ - [defaultParser addImageParsingWithImageFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range) { - // no additional formatting - } alternativeTextFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range) { - [attributedString addAttributes:weakParser.imageAttributes range:range]; + [defaultParser addImageParsingWithLinkFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSString * _Nullable link) { + UIImage *image = [UIImage imageNamed:link]; + if (image) { + NSTextAttachment *imageAttachment = [NSTextAttachment new]; + imageAttachment.image = image; + imageAttachment.bounds = CGRectMake(0, -5, image.size.width, image.size.height); + NSAttributedString *imgStr = [NSAttributedString attributedStringWithAttachment:imageAttachment]; + [attributedString replaceCharactersInRange:range withAttributedString:imgStr]; + } else { + if (!weakParser.skipLinkAttribute) { + NSURL *url = [NSURL URLWithString:link] ?: [NSURL URLWithString: + [link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + if (url.scheme) { + [attributedString addAttribute:NSLinkAttributeName + value:url + range:range]; + } + } + [attributedString addAttributes:weakParser.imageAttributes range:range]; + } }]; - [defaultParser addLinkParsingWithFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range) { + [defaultParser addLinkParsingWithLinkFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSString * _Nullable link) { + if (!weakParser.skipLinkAttribute) { + NSURL *url = [NSURL URLWithString:link] ?: [NSURL URLWithString: + [link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + if (url) { + [attributedString addAttribute:NSLinkAttributeName + value:url + range:range]; + } + } [attributedString addAttributes:weakParser.linkAttributes range:range]; }]; /* autodetection */ - [defaultParser addLinkDetectionWithFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range) { + [defaultParser addLinkDetectionWithLinkFormattingBlock:^(NSMutableAttributedString *attributedString, NSRange range, NSString * _Nullable link) { + if (!weakParser.skipLinkAttribute) { + [attributedString addAttribute:NSLinkAttributeName + value:[NSURL URLWithString:link] + range:range]; + } [attributedString addAttributes:weakParser.linkAttributes range:range]; }]; @@ -255,7 +285,26 @@ - (void)addImageParsingWithImageFormattingBlock:(TSMarkdownParserFormattingBlock }]; } -- (void)addLinkParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock { +- (void)addImageParsingWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock { + NSRegularExpression *headerExpression = [NSRegularExpression regularExpressionWithPattern:TSMarkdownImageRegex options:NSRegularExpressionDotMatchesLineSeparators error:nil]; + [self addParsingRuleWithRegularExpression:headerExpression block:^(NSTextCheckingResult *match, NSMutableAttributedString *attributedString) { + NSUInteger imagePathStart = [attributedString.string rangeOfString:@"(" options:(NSStringCompareOptions)0 range:match.range].location; + NSRange linkRange = NSMakeRange(imagePathStart, match.range.length + match.range.location - imagePathStart - 1); + NSString *imagePath = [attributedString.string substringWithRange:NSMakeRange(linkRange.location + 1, linkRange.length - 1)]; + + // deleting trailing markdown + // needs to be called before formattingBlock to support modification of length + [attributedString deleteCharactersInRange:NSMakeRange(linkRange.location - 1, linkRange.length + 2)]; + // deleting leading markdown + // needs to be called before formattingBlock to provide a stable range + [attributedString deleteCharactersInRange:NSMakeRange(match.range.location, 2)]; + // formatting link + // needs to be called last (may alter the length and needs range to be stable) + formattingBlock(attributedString, NSMakeRange(match.range.location, imagePathStart - match.range.location - 3), imagePath); + }]; +} + +- (void)addLinkParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock __attribute__((deprecated("use addLinkParsingWithLinkFormattingBlock: instead"))) { NSRegularExpression *linkParsing = [NSRegularExpression regularExpressionWithPattern:TSMarkdownLinkRegex options:NSRegularExpressionDotMatchesLineSeparators error:nil]; [self addParsingRuleWithRegularExpression:linkParsing block:^(NSTextCheckingResult *match, NSMutableAttributedString *attributedString) { @@ -281,6 +330,26 @@ - (void)addLinkParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)forma }]; } +- (void)addLinkParsingWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock { + NSRegularExpression *linkParsing = [NSRegularExpression regularExpressionWithPattern:TSMarkdownLinkRegex options:NSRegularExpressionDotMatchesLineSeparators error:nil]; + + [self addParsingRuleWithRegularExpression:linkParsing block:^(NSTextCheckingResult *match, NSMutableAttributedString *attributedString) { + NSUInteger linkStartInResult = [attributedString.string rangeOfString:@"(" options:NSBackwardsSearch range:match.range].location; + NSRange linkRange = NSMakeRange(linkStartInResult, match.range.length + match.range.location - linkStartInResult - 1); + NSString *linkURLString = [attributedString.string substringWithRange:NSMakeRange(linkRange.location + 1, linkRange.length - 1)]; + + // deleting trailing markdown + // needs to be called before formattingBlock to support modification of length + [attributedString deleteCharactersInRange:NSMakeRange(linkRange.location - 1, linkRange.length + 2)]; + // deleting leading markdown + // needs to be called before formattingBlock to provide a stable range + [attributedString deleteCharactersInRange:NSMakeRange(match.range.location, 1)]; + // formatting link + // needs to be called last (may alter the length and needs range to be stable) + formattingBlock(attributedString, NSMakeRange(match.range.location, linkStartInResult - match.range.location - 2), linkURLString); + }]; +} + #pragma mark inline parsing // pattern matching should be three parts: (leadingMD)(string)(trailingMD) @@ -310,7 +379,7 @@ - (void)addEmphasisParsingWithFormattingBlock:(TSMarkdownParserFormattingBlock)f #pragma mark link detection -- (void)addLinkDetectionWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock { +- (void)addLinkDetectionWithFormattingBlock:(TSMarkdownParserFormattingBlock)formattingBlock __attribute__((deprecated("use addLinkDetectionWithLinkFormattingBlock: instead"))) { NSDataDetector *linkDataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; [self addParsingRuleWithRegularExpression:linkDataDetector block:^(NSTextCheckingResult *match, NSMutableAttributedString *attributedString) { NSString *linkURLString = [attributedString.string substringWithRange:match.range]; @@ -321,6 +390,14 @@ - (void)addLinkDetectionWithFormattingBlock:(TSMarkdownParserFormattingBlock)for }]; } +- (void)addLinkDetectionWithLinkFormattingBlock:(TSMarkdownParserLinkFormattingBlock)formattingBlock { + NSDataDetector *linkDataDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil]; + [self addParsingRuleWithRegularExpression:linkDataDetector block:^(NSTextCheckingResult *match, NSMutableAttributedString *attributedString) { + NSString *linkURLString = [attributedString.string substringWithRange:match.range]; + formattingBlock(attributedString, match.range, linkURLString); + }]; +} + #pragma mark unescaping parsing + (NSString *)stringWithHexaString:(NSString *)hexaString atIndex:(NSUInteger)i { diff --git a/TSMarkdownParserExample iOS/AppDelegate.m b/TSMarkdownParserExample iOS/AppDelegate.m index d428e6c..8862c76 100644 --- a/TSMarkdownParserExample iOS/AppDelegate.m +++ b/TSMarkdownParserExample iOS/AppDelegate.m @@ -14,7 +14,7 @@ @interface AppDelegate () @implementation AppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { +- (BOOL)application:(UIApplication *)__unused application didFinishLaunchingWithOptions:(NSDictionary *)__unused launchOptions { // Override point for customization after application launch. return YES; } diff --git a/TSMarkdownParserExample iOS/Base.lproj/Main.storyboard b/TSMarkdownParserExample iOS/Base.lproj/Main.storyboard index b75c834..38c5a66 100644 --- a/TSMarkdownParserExample iOS/Base.lproj/Main.storyboard +++ b/TSMarkdownParserExample iOS/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -18,19 +18,19 @@ - + - + # header ###### header @@ -50,48 +50,72 @@ http://example.net - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + + diff --git a/TSMarkdownParserExample iOS/ViewController.m b/TSMarkdownParserExample iOS/ViewController.m index fb4a14e..c43e444 100644 --- a/TSMarkdownParserExample iOS/ViewController.m +++ b/TSMarkdownParserExample iOS/ViewController.m @@ -14,13 +14,19 @@ @interface ViewController () @property (strong, nonatomic) TSMarkdownParser *parser; @property (weak, nonatomic) IBOutlet UITextView *markdownInput; -@property (weak, nonatomic) IBOutlet UILabel *markdownOutput; +@property (weak, nonatomic) IBOutlet UILabel *markdownOutputLabel; +@property (weak, nonatomic) IBOutlet UITextView *markdownOutputTextView; @end @implementation ViewController +- (BOOL)prefersStatusBarHidden +{ + return YES; +} + - (void)viewDidLoad { [super viewDidLoad]; @@ -39,7 +45,19 @@ - (void)viewDidLoad - (void)textViewDidChange:(UITextView *)textView { - self.markdownOutput.attributedText = [self.parser attributedStringFromMarkdown:textView.text]; + NSAttributedString *result = [self.parser attributedStringFromMarkdown:textView.text]; + self.markdownOutputLabel.attributedText = result; + self.markdownOutputTextView.attributedText = result; +} + +- (IBAction)switchOutput:(UISegmentedControl *)segmentedControl { + if (segmentedControl.selectedSegmentIndex == 0) { + self.markdownOutputLabel.hidden = NO; + self.markdownOutputTextView.hidden = YES; + } else { + self.markdownOutputLabel.hidden = YES; + self.markdownOutputTextView.hidden = NO; + } } @end