diff --git a/QRichTextView/Assets/backcolor.png b/QRichTextView/Assets/backcolor.png new file mode 100644 index 0000000..794d5c1 Binary files /dev/null and b/QRichTextView/Assets/backcolor.png differ diff --git a/QRichTextView/Assets/bold.png b/QRichTextView/Assets/bold.png new file mode 100644 index 0000000..3c21979 Binary files /dev/null and b/QRichTextView/Assets/bold.png differ diff --git a/QRichTextView/Assets/bold@2x.png b/QRichTextView/Assets/bold@2x.png new file mode 100644 index 0000000..e834f1d Binary files /dev/null and b/QRichTextView/Assets/bold@2x.png differ diff --git a/QRichTextView/Assets/bullist.png b/QRichTextView/Assets/bullist.png new file mode 100644 index 0000000..cd84c95 Binary files /dev/null and b/QRichTextView/Assets/bullist.png differ diff --git a/QRichTextView/Assets/bullist@2x.png b/QRichTextView/Assets/bullist@2x.png new file mode 100644 index 0000000..7f90a64 Binary files /dev/null and b/QRichTextView/Assets/bullist@2x.png differ diff --git a/QRichTextView/Assets/button.png b/QRichTextView/Assets/button.png new file mode 100644 index 0000000..69c789d Binary files /dev/null and b/QRichTextView/Assets/button.png differ diff --git a/QRichTextView/Assets/buttonSelected.png b/QRichTextView/Assets/buttonSelected.png new file mode 100644 index 0000000..6d2ced6 Binary files /dev/null and b/QRichTextView/Assets/buttonSelected.png differ diff --git a/QRichTextView/Assets/buttoncenter.png b/QRichTextView/Assets/buttoncenter.png new file mode 100644 index 0000000..64cc64e Binary files /dev/null and b/QRichTextView/Assets/buttoncenter.png differ diff --git a/QRichTextView/Assets/buttoncenterSelected.png b/QRichTextView/Assets/buttoncenterSelected.png new file mode 100644 index 0000000..4b025fc Binary files /dev/null and b/QRichTextView/Assets/buttoncenterSelected.png differ diff --git a/QRichTextView/Assets/buttonleft.png b/QRichTextView/Assets/buttonleft.png new file mode 100644 index 0000000..1a15aae Binary files /dev/null and b/QRichTextView/Assets/buttonleft.png differ diff --git a/QRichTextView/Assets/buttonleftSelected.png b/QRichTextView/Assets/buttonleftSelected.png new file mode 100644 index 0000000..6486ed3 Binary files /dev/null and b/QRichTextView/Assets/buttonleftSelected.png differ diff --git a/QRichTextView/Assets/buttonright.png b/QRichTextView/Assets/buttonright.png new file mode 100644 index 0000000..6edab39 Binary files /dev/null and b/QRichTextView/Assets/buttonright.png differ diff --git a/QRichTextView/Assets/buttonrightSelected.png b/QRichTextView/Assets/buttonrightSelected.png new file mode 100644 index 0000000..e29ea61 Binary files /dev/null and b/QRichTextView/Assets/buttonrightSelected.png differ diff --git a/QRichTextView/Assets/code.png b/QRichTextView/Assets/code.png new file mode 100644 index 0000000..750878e Binary files /dev/null and b/QRichTextView/Assets/code.png differ diff --git a/QRichTextView/Assets/colors.jpg b/QRichTextView/Assets/colors.jpg new file mode 100644 index 0000000..20e8f2c Binary files /dev/null and b/QRichTextView/Assets/colors.jpg differ diff --git a/QRichTextView/Assets/dropDownTriangle.png b/QRichTextView/Assets/dropDownTriangle.png new file mode 100644 index 0000000..0657bc8 Binary files /dev/null and b/QRichTextView/Assets/dropDownTriangle.png differ diff --git a/QRichTextView/Assets/dropDownTriangle@2x.png b/QRichTextView/Assets/dropDownTriangle@2x.png new file mode 100644 index 0000000..164c501 Binary files /dev/null and b/QRichTextView/Assets/dropDownTriangle@2x.png differ diff --git a/QRichTextView/Assets/email.png b/QRichTextView/Assets/email.png new file mode 100644 index 0000000..cc79cab Binary files /dev/null and b/QRichTextView/Assets/email.png differ diff --git a/QRichTextView/Assets/file.png b/QRichTextView/Assets/file.png new file mode 100644 index 0000000..83ed38c Binary files /dev/null and b/QRichTextView/Assets/file.png differ diff --git a/QRichTextView/Assets/firstLineIndent.png b/QRichTextView/Assets/firstLineIndent.png new file mode 100644 index 0000000..14ce345 Binary files /dev/null and b/QRichTextView/Assets/firstLineIndent.png differ diff --git a/QRichTextView/Assets/firstLineIndent@2x.png b/QRichTextView/Assets/firstLineIndent@2x.png new file mode 100644 index 0000000..c1da40b Binary files /dev/null and b/QRichTextView/Assets/firstLineIndent@2x.png differ diff --git a/QRichTextView/Assets/forecolor.png b/QRichTextView/Assets/forecolor.png new file mode 100644 index 0000000..225a29a Binary files /dev/null and b/QRichTextView/Assets/forecolor.png differ diff --git a/QRichTextView/Assets/image.png b/QRichTextView/Assets/image.png new file mode 100644 index 0000000..2c29b36 Binary files /dev/null and b/QRichTextView/Assets/image.png differ diff --git a/QRichTextView/Assets/indent.png b/QRichTextView/Assets/indent.png new file mode 100644 index 0000000..e0f5e90 Binary files /dev/null and b/QRichTextView/Assets/indent.png differ diff --git a/QRichTextView/Assets/indent@2x.png b/QRichTextView/Assets/indent@2x.png new file mode 100644 index 0000000..3479634 Binary files /dev/null and b/QRichTextView/Assets/indent@2x.png differ diff --git a/QRichTextView/Assets/italic.png b/QRichTextView/Assets/italic.png new file mode 100644 index 0000000..f9982f1 Binary files /dev/null and b/QRichTextView/Assets/italic.png differ diff --git a/QRichTextView/Assets/italic@2x.png b/QRichTextView/Assets/italic@2x.png new file mode 100644 index 0000000..ccf7b18 Binary files /dev/null and b/QRichTextView/Assets/italic@2x.png differ diff --git a/QRichTextView/Assets/justifycenter.png b/QRichTextView/Assets/justifycenter.png new file mode 100644 index 0000000..bd76f35 Binary files /dev/null and b/QRichTextView/Assets/justifycenter.png differ diff --git a/QRichTextView/Assets/justifycenter@2x.png b/QRichTextView/Assets/justifycenter@2x.png new file mode 100644 index 0000000..8232942 Binary files /dev/null and b/QRichTextView/Assets/justifycenter@2x.png differ diff --git a/QRichTextView/Assets/justifyfull.png b/QRichTextView/Assets/justifyfull.png new file mode 100644 index 0000000..ee62452 Binary files /dev/null and b/QRichTextView/Assets/justifyfull.png differ diff --git a/QRichTextView/Assets/justifyfull@2x.png b/QRichTextView/Assets/justifyfull@2x.png new file mode 100644 index 0000000..2706960 Binary files /dev/null and b/QRichTextView/Assets/justifyfull@2x.png differ diff --git a/QRichTextView/Assets/justifyleft.png b/QRichTextView/Assets/justifyleft.png new file mode 100644 index 0000000..5180469 Binary files /dev/null and b/QRichTextView/Assets/justifyleft.png differ diff --git a/QRichTextView/Assets/justifyleft@2x.png b/QRichTextView/Assets/justifyleft@2x.png new file mode 100644 index 0000000..ebaf881 Binary files /dev/null and b/QRichTextView/Assets/justifyleft@2x.png differ diff --git a/QRichTextView/Assets/justifyright.png b/QRichTextView/Assets/justifyright.png new file mode 100644 index 0000000..e281ef4 Binary files /dev/null and b/QRichTextView/Assets/justifyright.png differ diff --git a/QRichTextView/Assets/justifyright@2x.png b/QRichTextView/Assets/justifyright@2x.png new file mode 100644 index 0000000..e1401ed Binary files /dev/null and b/QRichTextView/Assets/justifyright@2x.png differ diff --git a/QRichTextView/Assets/link.png b/QRichTextView/Assets/link.png new file mode 100644 index 0000000..b261c63 Binary files /dev/null and b/QRichTextView/Assets/link.png differ diff --git a/QRichTextView/Assets/numlist.png b/QRichTextView/Assets/numlist.png new file mode 100644 index 0000000..d6b1d11 Binary files /dev/null and b/QRichTextView/Assets/numlist.png differ diff --git a/QRichTextView/Assets/numlist@2x.png b/QRichTextView/Assets/numlist@2x.png new file mode 100644 index 0000000..98b9d4c Binary files /dev/null and b/QRichTextView/Assets/numlist@2x.png differ diff --git a/QRichTextView/Assets/outdent.png b/QRichTextView/Assets/outdent.png new file mode 100644 index 0000000..50ef607 Binary files /dev/null and b/QRichTextView/Assets/outdent.png differ diff --git a/QRichTextView/Assets/outdent@2x.png b/QRichTextView/Assets/outdent@2x.png new file mode 100644 index 0000000..38afd72 Binary files /dev/null and b/QRichTextView/Assets/outdent@2x.png differ diff --git a/QRichTextView/Assets/popoverArrowDown.png b/QRichTextView/Assets/popoverArrowDown.png new file mode 100755 index 0000000..b632256 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowDown.png differ diff --git a/QRichTextView/Assets/popoverArrowDown@2x.png b/QRichTextView/Assets/popoverArrowDown@2x.png new file mode 100755 index 0000000..8b79116 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowDown@2x.png differ diff --git a/QRichTextView/Assets/popoverArrowDownSimple.png b/QRichTextView/Assets/popoverArrowDownSimple.png new file mode 100755 index 0000000..ddae902 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowDownSimple.png differ diff --git a/QRichTextView/Assets/popoverArrowLeft.png b/QRichTextView/Assets/popoverArrowLeft.png new file mode 100755 index 0000000..d55ef2b Binary files /dev/null and b/QRichTextView/Assets/popoverArrowLeft.png differ diff --git a/QRichTextView/Assets/popoverArrowLeft@2x.png b/QRichTextView/Assets/popoverArrowLeft@2x.png new file mode 100755 index 0000000..f9d055a Binary files /dev/null and b/QRichTextView/Assets/popoverArrowLeft@2x.png differ diff --git a/QRichTextView/Assets/popoverArrowLeftSimple.png b/QRichTextView/Assets/popoverArrowLeftSimple.png new file mode 100755 index 0000000..adc4a8a Binary files /dev/null and b/QRichTextView/Assets/popoverArrowLeftSimple.png differ diff --git a/QRichTextView/Assets/popoverArrowRight.png b/QRichTextView/Assets/popoverArrowRight.png new file mode 100755 index 0000000..f549522 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowRight.png differ diff --git a/QRichTextView/Assets/popoverArrowRight@2x.png b/QRichTextView/Assets/popoverArrowRight@2x.png new file mode 100755 index 0000000..84af6f5 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowRight@2x.png differ diff --git a/QRichTextView/Assets/popoverArrowRightSimple.png b/QRichTextView/Assets/popoverArrowRightSimple.png new file mode 100755 index 0000000..b3ebfe3 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowRightSimple.png differ diff --git a/QRichTextView/Assets/popoverArrowUp.png b/QRichTextView/Assets/popoverArrowUp.png new file mode 100755 index 0000000..14adae0 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowUp.png differ diff --git a/QRichTextView/Assets/popoverArrowUp@2x.png b/QRichTextView/Assets/popoverArrowUp@2x.png new file mode 100755 index 0000000..40428f5 Binary files /dev/null and b/QRichTextView/Assets/popoverArrowUp@2x.png differ diff --git a/QRichTextView/Assets/popoverArrowUpSimple.png b/QRichTextView/Assets/popoverArrowUpSimple.png new file mode 100755 index 0000000..099d54f Binary files /dev/null and b/QRichTextView/Assets/popoverArrowUpSimple.png differ diff --git a/QRichTextView/Assets/popoverBg.png b/QRichTextView/Assets/popoverBg.png new file mode 100755 index 0000000..8fb6754 Binary files /dev/null and b/QRichTextView/Assets/popoverBg.png differ diff --git a/QRichTextView/Assets/popoverBg@2x.png b/QRichTextView/Assets/popoverBg@2x.png new file mode 100755 index 0000000..e1e575b Binary files /dev/null and b/QRichTextView/Assets/popoverBg@2x.png differ diff --git a/QRichTextView/Assets/popoverBgSimple.png b/QRichTextView/Assets/popoverBgSimple.png new file mode 100755 index 0000000..3cdf457 Binary files /dev/null and b/QRichTextView/Assets/popoverBgSimple.png differ diff --git a/QRichTextView/Assets/redo.png b/QRichTextView/Assets/redo.png new file mode 100644 index 0000000..62d04c4 Binary files /dev/null and b/QRichTextView/Assets/redo.png differ diff --git a/QRichTextView/Assets/removeformat.png b/QRichTextView/Assets/removeformat.png new file mode 100644 index 0000000..1a2b6dc Binary files /dev/null and b/QRichTextView/Assets/removeformat.png differ diff --git a/QRichTextView/Assets/strikethrough.png b/QRichTextView/Assets/strikethrough.png new file mode 100644 index 0000000..7f83663 Binary files /dev/null and b/QRichTextView/Assets/strikethrough.png differ diff --git a/QRichTextView/Assets/strikethrough@2x.png b/QRichTextView/Assets/strikethrough@2x.png new file mode 100644 index 0000000..9d93c53 Binary files /dev/null and b/QRichTextView/Assets/strikethrough@2x.png differ diff --git a/QRichTextView/Assets/underline.png b/QRichTextView/Assets/underline.png new file mode 100644 index 0000000..627f8d2 Binary files /dev/null and b/QRichTextView/Assets/underline.png differ diff --git a/QRichTextView/Assets/underline@2x.png b/QRichTextView/Assets/underline@2x.png new file mode 100644 index 0000000..502a40f Binary files /dev/null and b/QRichTextView/Assets/underline@2x.png differ diff --git a/QRichTextView/Assets/undo.png b/QRichTextView/Assets/undo.png new file mode 100644 index 0000000..2ab62b6 Binary files /dev/null and b/QRichTextView/Assets/undo.png differ diff --git a/QRichTextView/Assets/unlink.png b/QRichTextView/Assets/unlink.png new file mode 100644 index 0000000..a5913e0 Binary files /dev/null and b/QRichTextView/Assets/unlink.png differ diff --git a/QRichTextView/Classes/NSAttributedString+RichTextEditor.h b/QRichTextView/Classes/NSAttributedString+RichTextEditor.h new file mode 100644 index 0000000..423f99c --- /dev/null +++ b/QRichTextView/Classes/NSAttributedString+RichTextEditor.h @@ -0,0 +1,37 @@ +// +// NSAttributedString+RichTextEditor.h +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import "UIFont+RichTextEditor.h" + +@interface NSAttributedString (RichTextEditor) + +- (NSRange)firstParagraphRangeFromTextRange:(NSRange)range; +- (NSArray *)rangeOfParagraphsFromTextRange:(NSRange)textRange; +- (NSString *)htmlString; + +@end diff --git a/QRichTextView/Classes/NSAttributedString+RichTextEditor.m b/QRichTextView/Classes/NSAttributedString+RichTextEditor.m new file mode 100644 index 0000000..15052e2 --- /dev/null +++ b/QRichTextView/Classes/NSAttributedString+RichTextEditor.m @@ -0,0 +1,220 @@ +// +// NSAttributedString+RichTextEditor.m +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "NSAttributedString+RichTextEditor.h" + +@implementation NSAttributedString (RichTextEditor) + +#pragma mark - Public MEthods - + +- (NSRange)firstParagraphRangeFromTextRange:(NSRange)range +{ + NSInteger start = -1; + NSInteger end = -1; + NSInteger length = 0; + + NSInteger startingRange = (range.location == self.string.length || [self.string characterAtIndex:range.location] == '\n') ? + range.location-1 : + range.location; + + for (int i=startingRange ; i>=0 ; i--) + { + char c = [self.string characterAtIndex:i]; + if (c == '\n') + { + start = i+1; + break; + } + } + + start = (start == -1) ? 0 : start; + + NSInteger moveForwardIndex = (range.location > start) ? range.location : start; + + for (int i=moveForwardIndex; i<= self.string.length-1 ; i++) + { + char c = [self.string characterAtIndex:i]; + if (c == '\n') + { + end = i; + break; + } + } + + end = (end == -1) ? self.string.length : end; + length = end - start; + + return NSMakeRange(start, length); +} + +- (NSArray *)rangeOfParagraphsFromTextRange:(NSRange)textRange +{ + NSMutableArray *paragraphRanges = [NSMutableArray array]; + NSInteger rangeStartIndex = textRange.location; + + while (true) + { + NSRange range = [self firstParagraphRangeFromTextRange:NSMakeRange(rangeStartIndex, 0)]; + rangeStartIndex = range.location + range.length + 1; + + [paragraphRanges addObject:[NSValue valueWithRange:range]]; + + if (range.location + range.length >= textRange.location + textRange.length) + break; + } + + return paragraphRanges; +} + +- (NSString *)htmlString +{ + NSMutableString *htmlString = [NSMutableString string]; + NSArray *paragraphRanges = [self rangeOfParagraphsFromTextRange:NSMakeRange(0, self.string.length-1)]; + + for (int i=0 ; i 0) + [htmlString appendFormat:@"text-indent:%.0fpx; ", paragraphStyle.firstLineHeadIndent - paragraphStyle.headIndent]; + + if (paragraphStyle.headIndent > 0) + [htmlString appendFormat:@"margin-left:%.0fpx; ", paragraphStyle.headIndent]; + + + [htmlString appendString:@" \">"]; + + [self enumerateAttributesInRange:range + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSDictionary *dictionary, NSRange range, BOOL *stop){ + + NSMutableString *fontString = [NSMutableString string]; + UIFont *font = [dictionary objectForKey:NSFontAttributeName]; + UIColor *foregroundColor = [dictionary objectForKey:NSForegroundColorAttributeName]; + UIColor *backGroundColor = [dictionary objectForKey:NSBackgroundColorAttributeName]; + NSNumber *underline = [dictionary objectForKey:NSUnderlineStyleAttributeName]; + BOOL hasUnderline = (!underline || underline.intValue == NSUnderlineStyleNone) ? NO :YES; + NSNumber *strikeThrough = [dictionary objectForKey:NSStrikethroughStyleAttributeName]; + BOOL hasStrikeThrough = (!strikeThrough || strikeThrough.intValue == NSUnderlineStyleNone) ? NO :YES; + + [fontString appendFormat:@""]; + [fontString appendString:[[self.string substringFromIndex:range.location] substringToIndex:range.length]]; + [fontString appendString:@""]; + + if ([font isBold]) + { + [fontString insertString:@"" atIndex:0]; + [fontString insertString:@"" atIndex:fontString.length]; + } + + if ([font isItalic]) + { + [fontString insertString:@"" atIndex:0]; + [fontString insertString:@"" atIndex:fontString.length]; + } + + if (hasUnderline) + { + [fontString insertString:@"" atIndex:0]; + [fontString insertString:@"" atIndex:fontString.length]; + } + + if (hasStrikeThrough) + { + [fontString insertString:@"" atIndex:0]; + [fontString insertString:@"" atIndex:fontString.length]; + } + + + [htmlString appendString:fontString]; + }]; + + [htmlString appendString:@"

"]; + } + + return htmlString; +} + +#pragma mark - Helper Methods - + +- (NSString *)htmlTextAlignmentString:(NSTextAlignment)textAlignment +{ + switch (textAlignment) + { + case NSTextAlignmentLeft: + return @"left"; + + case NSTextAlignmentCenter: + return @"center"; + + case NSTextAlignmentRight: + return @"right"; + + case NSTextAlignmentJustified: + return @"justify"; + + default: + return nil; + } +} + +- (NSString *)htmlRgbColor:(UIColor *)color +{ + CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + return [NSString stringWithFormat:@"rgb(%d,%d,%d)",(int)(red*255.0), (int)(green*255.0), (int)(blue*255.0)]; +} + +@end diff --git a/QRichTextView/Classes/UIBarButtonItem+WEPopover.h b/QRichTextView/Classes/UIBarButtonItem+WEPopover.h new file mode 100755 index 0000000..4cf59bd --- /dev/null +++ b/QRichTextView/Classes/UIBarButtonItem+WEPopover.h @@ -0,0 +1,18 @@ +/* + * UIBarButtonItem+WEPopover.h + * WEPopover + * + * Created by Werner Altewischer on 07/05/11. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +#import +#import + +@interface UIBarButtonItem(WEPopover) + +- (CGRect)frameInView:(UIView *)v; +- (UIView *)superview; + +@end diff --git a/QRichTextView/Classes/UIBarButtonItem+WEPopover.m b/QRichTextView/Classes/UIBarButtonItem+WEPopover.m new file mode 100755 index 0000000..52b13d2 --- /dev/null +++ b/QRichTextView/Classes/UIBarButtonItem+WEPopover.m @@ -0,0 +1,46 @@ +/* + * UIBarButtonItem+WEPopover.m + * WEPopover + * + * Created by Werner Altewischer on 07/05/11. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +#import "UIBarButtonItem+WEPopover.h" + +@implementation UIBarButtonItem(WEPopover) + +- (CGRect)frameInView:(UIView *)v { + + UIView *theView = self.customView; + if (!theView && [self respondsToSelector:@selector(view)]) { + theView = [self performSelector:@selector(view)]; + } + + UIView *parentView = theView.superview; + NSArray *subviews = parentView.subviews; + + NSUInteger indexOfView = [subviews indexOfObject:theView]; + NSUInteger subviewCount = subviews.count; + + if (subviewCount > 0 && indexOfView != NSNotFound) { + UIView *button = [parentView.subviews objectAtIndex:indexOfView]; + return [button convertRect:button.bounds toView:v]; + } else { + return CGRectZero; + } +} + +- (UIView *)superview { + + UIView *theView = self.customView; + if (!theView && [self respondsToSelector:@selector(view)]) { + theView = [self performSelector:@selector(view)]; + } + + UIView *parentView = theView.superview; + return parentView; +} + +@end diff --git a/QRichTextView/Classes/UIFont+RichTextEditor.h b/QRichTextView/Classes/UIFont+RichTextEditor.h new file mode 100644 index 0000000..a56acf5 --- /dev/null +++ b/QRichTextView/Classes/UIFont+RichTextEditor.h @@ -0,0 +1,40 @@ +// +// UIFont+RichTextEditor.h +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +@interface UIFont (RichTextEditor) + ++ (NSString *)postscriptNameFromFullName:(NSString *)fullName; ++ (UIFont *)fontWithName:(NSString *)name size:(CGFloat)size boldTrait:(BOOL)isBold italicTrait:(BOOL)isItalic; +- (UIFont *)fontWithBoldTrait:(BOOL)bold italicTrait:(BOOL)italic andSize:(CGFloat)size; +- (UIFont *)fontWithBoldTrait:(BOOL)bold andItalicTrait:(BOOL)italic; +- (BOOL)isBold; +- (BOOL)isItalic; + +@end diff --git a/QRichTextView/Classes/UIFont+RichTextEditor.m b/QRichTextView/Classes/UIFont+RichTextEditor.m new file mode 100644 index 0000000..10b95ce --- /dev/null +++ b/QRichTextView/Classes/UIFont+RichTextEditor.m @@ -0,0 +1,103 @@ +// +// UIFont+RichTextEditor.m +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIFont+RichTextEditor.h" + +@implementation UIFont (RichTextEditor) + ++ (NSString *)postscriptNameFromFullName:(NSString *)fullName +{ + UIFont *font = [UIFont fontWithName:fullName size:1]; + return (__bridge NSString *)(CTFontCopyPostScriptName((__bridge CTFontRef)(font))); +} + ++ (UIFont *)fontWithName:(NSString *)name size:(CGFloat)size boldTrait:(BOOL)isBold italicTrait:(BOOL)isItalic +{ + NSString *postScriptName = [UIFont postscriptNameFromFullName:name]; + + CTFontSymbolicTraits traits = 0; + CTFontRef newFontRef; + CTFontRef fontWithoutTrait = CTFontCreateWithName((__bridge CFStringRef)(postScriptName), size, NULL); + + if (isItalic) + traits |= kCTFontItalicTrait; + + if (isBold) + traits |= kCTFontBoldTrait; + + if (traits == 0) + { + newFontRef= CTFontCreateCopyWithAttributes(fontWithoutTrait, 0.0, NULL, NULL); + } + else + { + newFontRef = CTFontCreateCopyWithSymbolicTraits(fontWithoutTrait, 0.0, NULL, traits, traits); + } + + if (newFontRef) + { + NSString *fontNameKey = (__bridge NSString *)(CTFontCopyName(newFontRef, kCTFontPostScriptNameKey)); + return [UIFont fontWithName:fontNameKey size:CTFontGetSize(newFontRef)]; + } + + return nil; +} + +- (UIFont *)fontWithBoldTrait:(BOOL)bold italicTrait:(BOOL)italic andSize:(CGFloat)size +{ + CTFontRef fontRef = (__bridge CTFontRef)self; + NSString *familyName = (__bridge NSString *)(CTFontCopyName(fontRef, kCTFontFamilyNameKey)); + NSString *postScriptName = [UIFont postscriptNameFromFullName:familyName]; + return [[self class] fontWithName:postScriptName size:size boldTrait:bold italicTrait:italic]; +} + +- (UIFont *)fontWithBoldTrait:(BOOL)bold andItalicTrait:(BOOL)italic +{ + return [self fontWithBoldTrait:bold italicTrait:italic andSize:self.pointSize]; +} + +- (BOOL)isBold +{ + CTFontSymbolicTraits trait = CTFontGetSymbolicTraits((__bridge CTFontRef)self); + + if ((trait & kCTFontTraitBold) == kCTFontTraitBold) + return YES; + + return NO; +} + +- (BOOL)isItalic +{ + CTFontSymbolicTraits trait = CTFontGetSymbolicTraits((__bridge CTFontRef)self); + + if ((trait & kCTFontTraitItalic) == kCTFontTraitItalic) + return YES; + + return NO; +} + +@end diff --git a/QRichTextView/Classes/UIView+RichTextEditor.h b/QRichTextView/Classes/UIView+RichTextEditor.h new file mode 100644 index 0000000..4c00b28 --- /dev/null +++ b/QRichTextView/Classes/UIView+RichTextEditor.h @@ -0,0 +1,36 @@ +// +// UIView+RichTextEditor.h +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import + +@interface UIView (RichTextEditor) + +- (UIColor *)colorOfPoint:(CGPoint)point; +- (UIViewController *)firstAvailableViewController; + +@end diff --git a/QRichTextView/Classes/UIView+RichTextEditor.m b/QRichTextView/Classes/UIView+RichTextEditor.m new file mode 100644 index 0000000..831cefe --- /dev/null +++ b/QRichTextView/Classes/UIView+RichTextEditor.m @@ -0,0 +1,72 @@ +// +// UIView+RichTextEditor.m +// RichTextEdtor +// +// Created by Aryan Gh on 7/21/13. +// Copyright (c) 2013 Aryan Ghassemi. All rights reserved. +// +// https://github.com/aryaxt/iOS-Rich-Text-Editor +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "UIView+RichTextEditor.h" + +@implementation UIView (RichTextEditor) + +- (UIColor *)colorOfPoint:(CGPoint)point +{ + unsigned char pixel[4] = {0}; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, (uint32_t)kCGImageAlphaPremultipliedLast); + + CGContextTranslateCTM(context, -point.x, -point.y); + + [self.layer renderInContext:context]; + + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0]; + + return color; +} + +- (UIViewController *)firstAvailableViewController +{ + return (UIViewController *)[self traverseResponderChainForUIViewController]; +} + +- (id)traverseResponderChainForUIViewController +{ + id nextResponder = [self nextResponder]; + + if ([nextResponder isKindOfClass:[UIViewController class]]) + { + return nextResponder; + } + else if ([nextResponder isKindOfClass:[UIView class]]) + { + return [nextResponder traverseResponderChainForUIViewController]; + } + else + { + return nil; + } +} + +@end diff --git a/QRichTextView/Classes/WEPopoverContainerView.h b/QRichTextView/Classes/WEPopoverContainerView.h new file mode 100755 index 0000000..ffe32a6 --- /dev/null +++ b/QRichTextView/Classes/WEPopoverContainerView.h @@ -0,0 +1,100 @@ +// +// WEPopoverContainerView.h +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import +#import + +/** + * @brief Properties for the container view determining the area where the actual content view can/may be displayed. Also Images can be supplied for the arrow images and background. + */ +@interface WEPopoverContainerViewProperties : NSObject +{ + NSString *bgImageName; + NSString *upArrowImageName; + NSString *downArrowImageName; + NSString *leftArrowImageName; + NSString *rightArrowImageName; + CGFloat leftBgMargin; + CGFloat rightBgMargin; + CGFloat topBgMargin; + CGFloat bottomBgMargin; + NSInteger topBgCapSize; + NSInteger leftBgCapSize; + CGFloat arrowMargin; +} + +@property(nonatomic, retain) NSString *bgImageName; +@property(nonatomic, retain) NSString *upArrowImageName; +@property(nonatomic, retain) NSString *downArrowImageName; +@property(nonatomic, retain) NSString *leftArrowImageName; +@property(nonatomic, retain) NSString *rightArrowImageName; +@property(nonatomic, assign) CGFloat leftBgMargin; +@property(nonatomic, assign) CGFloat rightBgMargin; +@property(nonatomic, assign) CGFloat topBgMargin; +@property(nonatomic, assign) CGFloat bottomBgMargin; +@property(nonatomic, assign) CGFloat leftContentMargin; +@property(nonatomic, assign) CGFloat rightContentMargin; +@property(nonatomic, assign) CGFloat topContentMargin; +@property(nonatomic, assign) CGFloat bottomContentMargin; +@property(nonatomic, assign) NSInteger topBgCapSize; +@property(nonatomic, assign) NSInteger leftBgCapSize; +@property(nonatomic, assign) CGFloat arrowMargin; + +@end + +@class WEPopoverContainerView; + +/** + * @brief Container/background view for displaying a popover view. + */ +@interface WEPopoverContainerView : UIView { + UIImage *bgImage; + UIImage *arrowImage; + + WEPopoverContainerViewProperties *properties; + + UIPopoverArrowDirection arrowDirection; + + CGRect arrowRect; + CGRect bgRect; + CGPoint offset; + CGPoint arrowOffset; + + CGSize correctedSize; + UIView *contentView; +} + +/** + * @brief The current arrow direction for the popover. + */ +@property (nonatomic, readonly) UIPopoverArrowDirection arrowDirection; + +/** + * @brief The content view being displayed. + */ +@property (nonatomic, retain) UIView *contentView; + +/** + * @brief Initializes the position of the popover with a size, anchor rect, display area and permitted arrow directions and optionally the properties. + * If the last is not supplied the defaults are taken (requires images to be present in bundle representing a black rounded background with partial transparency). + */ +- (id)initWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea +permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections + properties:(WEPopoverContainerViewProperties *)properties; + +/** + * @brief To update the position of the popover with a new anchor rect, display area and permitted arrow directions + */ +- (void)updatePositionWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea + permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections; + +@end diff --git a/QRichTextView/Classes/WEPopoverContainerView.m b/QRichTextView/Classes/WEPopoverContainerView.m new file mode 100755 index 0000000..d037050 --- /dev/null +++ b/QRichTextView/Classes/WEPopoverContainerView.m @@ -0,0 +1,367 @@ +// +// WEPopoverContainerViewProperties.m +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import "WEPopoverContainerView.h" + +@implementation WEPopoverContainerViewProperties + +@synthesize bgImageName, upArrowImageName, downArrowImageName, leftArrowImageName, rightArrowImageName, topBgMargin, bottomBgMargin, leftBgMargin, rightBgMargin, topBgCapSize, leftBgCapSize; +@synthesize leftContentMargin, rightContentMargin, topContentMargin, bottomContentMargin, arrowMargin; + +//- (void)dealloc { +// self.bgImageName = nil; +// self.upArrowImageName = nil; +// self.downArrowImageName = nil; +// self.leftArrowImageName = nil; +// self.rightArrowImageName = nil; +// [super dealloc]; +//} + +@end + +@interface WEPopoverContainerView(Private) + +- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections; +- (CGRect)contentRect; +- (CGSize)contentSize; +- (void)setProperties:(WEPopoverContainerViewProperties *)props; +- (void)initFrame; + +@end + +@implementation WEPopoverContainerView + +@synthesize arrowDirection, contentView; + +- (id)initWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea +permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections + properties:(WEPopoverContainerViewProperties *)theProperties { + if ((self = [super initWithFrame:CGRectZero])) { + + [self setProperties:theProperties]; + correctedSize = CGSizeMake(theSize.width + properties.leftBgMargin + properties.rightBgMargin + properties.leftContentMargin + properties.rightContentMargin, + theSize.height + properties.topBgMargin + properties.bottomBgMargin + properties.topContentMargin + properties.bottomContentMargin); + [self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections]; + [self initFrame]; + self.backgroundColor = [UIColor clearColor]; + UIImage *theImage = [UIImage imageNamed:properties.bgImageName]; + bgImage = [theImage stretchableImageWithLeftCapWidth:properties.leftBgCapSize topCapHeight:properties.topBgCapSize]; + + self.clipsToBounds = YES; + self.userInteractionEnabled = YES; + } + return self; +} + +//- (void)dealloc { +// [properties release]; +// [contentView release]; +// [bgImage release]; +// [arrowImage release]; +// [super dealloc]; +//} + +- (void)drawRect:(CGRect)rect { + [bgImage drawInRect:bgRect blendMode:kCGBlendModeNormal alpha:1.0]; + [arrowImage drawInRect:arrowRect blendMode:kCGBlendModeNormal alpha:1.0]; +} + +- (void)updatePositionWithSize:(CGSize)theSize + anchorRect:(CGRect)anchorRect + displayArea:(CGRect)displayArea + permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections { + + correctedSize = CGSizeMake(theSize.width + properties.leftBgMargin + properties.rightBgMargin + properties.leftContentMargin + properties.rightContentMargin, + theSize.height + properties.topBgMargin + properties.bottomBgMargin + properties.topContentMargin + properties.bottomContentMargin); + [self determineGeometryForSize:correctedSize anchorRect:anchorRect displayArea:displayArea permittedArrowDirections:permittedArrowDirections]; + [self initFrame]; + [self setNeedsDisplay]; + +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { + return CGRectContainsPoint(self.contentRect, point); +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + +} + +- (void)setContentView:(UIView *)v { + if (v != contentView) { +// [contentView release]; + contentView = v; + contentView.frame = self.contentRect; + [self addSubview:contentView]; + } +} + + + +@end + +@implementation WEPopoverContainerView(Private) + +- (void)initFrame { + CGRect theFrame = CGRectOffset(CGRectUnion(bgRect, arrowRect), offset.x, offset.y); + + //If arrow rect origin is < 0 the frame above is extended to include it so we should offset the other rects + arrowOffset = CGPointMake(MAX(0, -arrowRect.origin.x), MAX(0, -arrowRect.origin.y)); + bgRect = CGRectOffset(bgRect, arrowOffset.x, arrowOffset.y); + arrowRect = CGRectOffset(arrowRect, arrowOffset.x, arrowOffset.y); + + self.frame = CGRectIntegral(theFrame); +} + +- (CGSize)contentSize { + return self.contentRect.size; +} + +- (CGRect)contentRect { + CGRect rect = CGRectMake(properties.leftBgMargin + properties.leftContentMargin + arrowOffset.x, + properties.topBgMargin + properties.topContentMargin + arrowOffset.y, + bgRect.size.width - properties.leftBgMargin - properties.rightBgMargin - properties.leftContentMargin - properties.rightContentMargin, + bgRect.size.height - properties.topBgMargin - properties.bottomBgMargin - properties.topContentMargin - properties.bottomContentMargin); + return rect; +} + +- (void)setProperties:(WEPopoverContainerViewProperties *)props { + if (properties != props) { +// [properties release]; + properties = props; + } +} + +- (void)determineGeometryForSize:(CGSize)theSize anchorRect:(CGRect)anchorRect displayArea:(CGRect)displayArea permittedArrowDirections:(UIPopoverArrowDirection)supportedArrowDirections { + + //Determine the frame, it should not go outside the display area + UIPopoverArrowDirection theArrowDirection = UIPopoverArrowDirectionUp; + + offset = CGPointZero; + bgRect = CGRectZero; + arrowRect = CGRectZero; + arrowDirection = UIPopoverArrowDirectionUnknown; + + CGFloat biggestSurface = 0.0f; + CGFloat currentMinMargin = 0.0f; + + UIImage *upArrowImage = [UIImage imageNamed:properties.upArrowImageName]; + UIImage *downArrowImage = [UIImage imageNamed:properties.downArrowImageName]; + UIImage *leftArrowImage = [UIImage imageNamed:properties.leftArrowImageName]; + UIImage *rightArrowImage = [UIImage imageNamed:properties.rightArrowImageName]; + + while (theArrowDirection <= UIPopoverArrowDirectionRight) { + + if ((supportedArrowDirections & theArrowDirection)) { + + CGRect theBgRect = CGRectMake(0, 0, theSize.width, theSize.height); + CGRect theArrowRect = CGRectZero; + CGPoint theOffset = CGPointZero; + CGFloat xArrowOffset = 0.0; + CGFloat yArrowOffset = 0.0; + CGPoint anchorPoint = CGPointZero; + + switch (theArrowDirection) { + case UIPopoverArrowDirectionUp: + + anchorPoint = CGPointMake(CGRectGetMidX(anchorRect) - displayArea.origin.x, CGRectGetMaxY(anchorRect) - displayArea.origin.y); + + xArrowOffset = theSize.width / 2 - upArrowImage.size.width / 2; + yArrowOffset = properties.topBgMargin - upArrowImage.size.height; + + theOffset = CGPointMake(anchorPoint.x - xArrowOffset - upArrowImage.size.width / 2, anchorPoint.y - yArrowOffset); + + if (theOffset.x < 0) { + xArrowOffset += theOffset.x; + theOffset.x = 0; + } else if (theOffset.x + theSize.width > displayArea.size.width) { + xArrowOffset += (theOffset.x + theSize.width - displayArea.size.width); + theOffset.x = displayArea.size.width - theSize.width; + } + + //Cap the arrow offset + xArrowOffset = MAX(xArrowOffset, properties.leftBgMargin + properties.arrowMargin); + xArrowOffset = MIN(xArrowOffset, theSize.width - properties.rightBgMargin - properties.arrowMargin - upArrowImage.size.width); + + theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, upArrowImage.size.width, upArrowImage.size.height); + + break; + case UIPopoverArrowDirectionDown: + + anchorPoint = CGPointMake(CGRectGetMidX(anchorRect) - displayArea.origin.x, CGRectGetMinY(anchorRect) - displayArea.origin.y); + + xArrowOffset = theSize.width / 2 - downArrowImage.size.width / 2; + yArrowOffset = theSize.height - properties.bottomBgMargin; + + theOffset = CGPointMake(anchorPoint.x - xArrowOffset - downArrowImage.size.width / 2, anchorPoint.y - yArrowOffset - downArrowImage.size.height); + + if (theOffset.x < 0) { + xArrowOffset += theOffset.x; + theOffset.x = 0; + } else if (theOffset.x + theSize.width > displayArea.size.width) { + xArrowOffset += (theOffset.x + theSize.width - displayArea.size.width); + theOffset.x = displayArea.size.width - theSize.width; + } + + //Cap the arrow offset + xArrowOffset = MAX(xArrowOffset, properties.leftBgMargin + properties.arrowMargin); + xArrowOffset = MIN(xArrowOffset, theSize.width - properties.rightBgMargin - properties.arrowMargin - downArrowImage.size.width); + + theArrowRect = CGRectMake(xArrowOffset , yArrowOffset, downArrowImage.size.width, downArrowImage.size.height); + + break; + case UIPopoverArrowDirectionLeft: + + anchorPoint = CGPointMake(CGRectGetMaxX(anchorRect) - displayArea.origin.x, CGRectGetMidY(anchorRect) - displayArea.origin.y); + + xArrowOffset = properties.leftBgMargin - leftArrowImage.size.width; + yArrowOffset = theSize.height / 2 - leftArrowImage.size.height / 2; + + theOffset = CGPointMake(anchorPoint.x - xArrowOffset, anchorPoint.y - yArrowOffset - leftArrowImage.size.height / 2); + + if (theOffset.y < 0) { + yArrowOffset += theOffset.y; + theOffset.y = 0; + } else if (theOffset.y + theSize.height > displayArea.size.height) { + yArrowOffset += (theOffset.y + theSize.height - displayArea.size.height); + theOffset.y = displayArea.size.height - theSize.height; + } + + //Cap the arrow offset + yArrowOffset = MAX(yArrowOffset, properties.topBgMargin + properties.arrowMargin); + yArrowOffset = MIN(yArrowOffset, theSize.height - properties.bottomBgMargin - properties.arrowMargin - leftArrowImage.size.height); + + theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, leftArrowImage.size.width, leftArrowImage.size.height); + + break; + case UIPopoverArrowDirectionRight: + + anchorPoint = CGPointMake(CGRectGetMinX(anchorRect) - displayArea.origin.x, CGRectGetMidY(anchorRect) - displayArea.origin.y); + + xArrowOffset = theSize.width - properties.rightBgMargin; + yArrowOffset = theSize.height / 2 - rightArrowImage.size.width / 2; + + theOffset = CGPointMake(anchorPoint.x - xArrowOffset - rightArrowImage.size.width, anchorPoint.y - yArrowOffset - rightArrowImage.size.height / 2); + + if (theOffset.y < 0) { + yArrowOffset += theOffset.y; + theOffset.y = 0; + } else if (theOffset.y + theSize.height > displayArea.size.height) { + yArrowOffset += (theOffset.y + theSize.height - displayArea.size.height); + theOffset.y = displayArea.size.height - theSize.height; + } + + //Cap the arrow offset + yArrowOffset = MAX(yArrowOffset, properties.topBgMargin + properties.arrowMargin); + yArrowOffset = MIN(yArrowOffset, theSize.height - properties.bottomBgMargin - properties.arrowMargin - rightArrowImage.size.height); + + theArrowRect = CGRectMake(xArrowOffset, yArrowOffset, rightArrowImage.size.width, rightArrowImage.size.height); + + break; + default: + break; + } + + CGRect bgFrame = CGRectOffset(theBgRect, theOffset.x, theOffset.y); + + CGFloat minMarginLeft = CGRectGetMinX(bgFrame); + CGFloat minMarginRight = CGRectGetWidth(displayArea) - CGRectGetMaxX(bgFrame); + CGFloat minMarginTop = CGRectGetMinY(bgFrame); + CGFloat minMarginBottom = CGRectGetHeight(displayArea) - CGRectGetMaxY(bgFrame); + + if (minMarginLeft < 0) { + // Popover is too wide and clipped on the left; decrease width + // and move it to the right + theOffset.x -= minMarginLeft; + theBgRect.size.width += minMarginLeft; + minMarginLeft = 0; + if (theArrowDirection == UIPopoverArrowDirectionRight) { + theArrowRect.origin.x = CGRectGetMaxX(theBgRect) - properties.rightBgMargin; + } + } + if (minMarginRight < 0) { + // Popover is too wide and clipped on the right; decrease width. + theBgRect.size.width += minMarginRight; + minMarginRight = 0; + if (theArrowDirection == UIPopoverArrowDirectionLeft) { + theArrowRect.origin.x = CGRectGetMinX(theBgRect) - leftArrowImage.size.width + properties.leftBgMargin; + } + } + if (minMarginTop < 0) { + // Popover is too high and clipped at the top; decrease height + // and move it down + theOffset.y -= minMarginTop; + theBgRect.size.height += minMarginTop; + minMarginTop = 0; + if (theArrowDirection == UIPopoverArrowDirectionDown) { + theArrowRect.origin.y = CGRectGetMaxY(theBgRect) - properties.bottomBgMargin; + } + } + if (minMarginBottom < 0) { + // Popover is too high and clipped at the bottom; decrease height. + theBgRect.size.height += minMarginBottom; + minMarginBottom = 0; + if (theArrowDirection == UIPopoverArrowDirectionUp) { + theArrowRect.origin.y = CGRectGetMinY(theBgRect) - upArrowImage.size.height + properties.topBgMargin; + } + } + bgFrame = CGRectOffset(theBgRect, theOffset.x, theOffset.y); + + CGFloat minMargin = MIN(minMarginLeft, minMarginRight); + minMargin = MIN(minMargin, minMarginTop); + minMargin = MIN(minMargin, minMarginBottom); + + // Calculate intersection and surface + CGFloat surface = theBgRect.size.width * theBgRect.size.height; + + if (surface >= biggestSurface && minMargin >= currentMinMargin) { + biggestSurface = surface; + offset = CGPointMake(theOffset.x + displayArea.origin.x, theOffset.y + displayArea.origin.y); + arrowRect = theArrowRect; + bgRect = theBgRect; + arrowDirection = theArrowDirection; + currentMinMargin = minMargin; + } + } + + theArrowDirection <<= 1; + } + + switch (arrowDirection) { + case UIPopoverArrowDirectionUp: + arrowImage = upArrowImage; + break; + case UIPopoverArrowDirectionDown: + arrowImage = downArrowImage; + break; + case UIPopoverArrowDirectionLeft: + arrowImage = leftArrowImage; + break; + case UIPopoverArrowDirectionRight: + arrowImage = rightArrowImage; + break; + default: + break; + } +} + +@end diff --git a/QRichTextView/Classes/WEPopoverController.h b/QRichTextView/Classes/WEPopoverController.h new file mode 100755 index 0000000..068a799 --- /dev/null +++ b/QRichTextView/Classes/WEPopoverController.h @@ -0,0 +1,75 @@ +// +// WEPopoverController.h +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import +#import +#import "WEPopoverContainerView.h" +#import "WETouchableView.h" + +@class WEPopoverController; + +@protocol WEPopoverControllerDelegate + +- (void)popoverControllerDidDismissPopover:(WEPopoverController *)popoverController; +- (BOOL)popoverControllerShouldDismissPopover:(WEPopoverController *)popoverController; + +@end + +/** + * @brief Popover controller for the iPhone, mimicing the iPad UIPopoverController interface. See that class for more details. + */ +@interface WEPopoverController : NSObject { + UIViewController *contentViewController; + UIView *view; +// UIView *parentView; + WETouchableView *backgroundView; + + BOOL popoverVisible; + UIPopoverArrowDirection popoverArrowDirection; +// id delegate; + CGSize popoverContentSize; + WEPopoverContainerViewProperties *containerViewProperties; + id context; + NSArray *passthroughViews; +} + +@property(nonatomic, retain) UIViewController *contentViewController; + +@property (nonatomic, readonly) UIView *view; +@property (nonatomic, readonly, getter=isPopoverVisible) BOOL popoverVisible; +@property (nonatomic, readonly) UIPopoverArrowDirection popoverArrowDirection; +@property (nonatomic, weak) id delegate; +@property (nonatomic, assign) CGSize popoverContentSize; +@property (nonatomic, retain) WEPopoverContainerViewProperties *containerViewProperties; +@property (nonatomic, retain) id context; +@property (nonatomic, weak) UIView *parentView; +@property (nonatomic, copy) NSArray *passthroughViews; + +- (id)initWithContentViewController:(UIViewController *)theContentViewController; + +- (void)dismissPopoverAnimated:(BOOL)animated; + +- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated; + +- (void)presentPopoverFromRect:(CGRect)rect + inView:(UIView *)view + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated; + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)view + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections; + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)view + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated; + +@end diff --git a/QRichTextView/Classes/WEPopoverController.m b/QRichTextView/Classes/WEPopoverController.m new file mode 100755 index 0000000..2ef2b8e --- /dev/null +++ b/QRichTextView/Classes/WEPopoverController.m @@ -0,0 +1,377 @@ +// +// WEPopoverController.m +// WEPopover +// +// Created by Werner Altewischer on 02/09/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import "WEPopoverController.h" +#import "WEPopoverParentView.h" +#import "UIBarButtonItem+WEPopover.h" + +#define FADE_DURATION 0.3 + +@interface WEPopoverController(Private) + +- (UIView *)keyView; +- (void)updateBackgroundPassthroughViews; +- (void)setView:(UIView *)v; +- (CGRect)displayAreaForView:(UIView *)theView; +- (WEPopoverContainerViewProperties *)defaultContainerViewProperties; +- (void)dismissPopoverAnimated:(BOOL)animated userInitiated:(BOOL)userInitiated; + +@end + + +@implementation WEPopoverController + +@synthesize contentViewController; +@synthesize popoverContentSize; +@synthesize popoverVisible; +@synthesize popoverArrowDirection; +@synthesize delegate; +@synthesize view; +@synthesize parentView; +@synthesize containerViewProperties; +@synthesize context; +@synthesize passthroughViews; + +- (id)init { + if ((self = [super init])) { + } + return self; +} + +- (id)initWithContentViewController:(UIViewController *)viewController { + if ((self = [self init])) { + self.contentViewController = viewController; + } + return self; +} + +//- (void)dealloc { +// [self dismissPopoverAnimated:NO]; +// [contentViewController release]; +// [containerViewProperties release]; +// [passthroughViews release]; +// self.context = nil; +// [super dealloc]; +//} + +- (void)setContentViewController:(UIViewController *)vc { + if (vc != contentViewController) { +// [contentViewController release]; + contentViewController = vc; + popoverContentSize = CGSizeZero; + } +} + +- (BOOL)forwardAppearanceMethods { + return ![contentViewController respondsToSelector:@selector(automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers)]; +} + +//Overridden setter to copy the passthroughViews to the background view if it exists already +- (void)setPassthroughViews:(NSArray *)array { +// [passthroughViews release]; + passthroughViews = nil; + if (array) { + passthroughViews = [[NSArray alloc] initWithArray:array]; + } + [self updateBackgroundPassthroughViews]; +} + +- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)theContext { + + if ([animationID isEqual:@"FadeIn"]) { + self.view.userInteractionEnabled = YES; + popoverVisible = YES; + + if ([self forwardAppearanceMethods]) { + [contentViewController viewDidAppear:YES]; + } + } else if ([animationID isEqual:@"FadeOut"]) { + popoverVisible = NO; + + if ([self forwardAppearanceMethods]) { + [contentViewController viewDidDisappear:YES]; + } + [self.view removeFromSuperview]; + self.view = nil; + [backgroundView removeFromSuperview]; +// [backgroundView release]; + backgroundView = nil; + + BOOL userInitiatedDismissal = [(__bridge NSNumber *)theContext boolValue]; + + if (userInitiatedDismissal) { + //Only send message to delegate in case the user initiated this event, which is if he touched outside the view + [delegate popoverControllerDidDismissPopover:self]; + } + } +} + +- (void)dismissPopoverAnimated:(BOOL)animated { + + [self dismissPopoverAnimated:animated userInitiated:NO]; +} + +- (void)presentPopoverFromBarButtonItem:(UIBarButtonItem *)item + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated { + + UIView *v = [self keyView]; + CGRect rect = [item frameInView:v]; + + return [self presentPopoverFromRect:rect inView:v permittedArrowDirections:arrowDirections animated:animated]; +} + +- (void)presentPopoverFromRect:(CGRect)rect + inView:(UIView *)theView + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated { + + + [self dismissPopoverAnimated:NO]; + + //First force a load view for the contentViewController so the popoverContentSize is properly initialized + [contentViewController view]; + + if (CGSizeEqualToSize(popoverContentSize, CGSizeZero)) { + popoverContentSize = contentViewController.preferredContentSize; + } + + CGRect displayArea = [self displayAreaForView:theView]; + + WEPopoverContainerViewProperties *props = self.containerViewProperties ? self.containerViewProperties : [self defaultContainerViewProperties]; + WEPopoverContainerView *containerView = [[WEPopoverContainerView alloc] initWithSize:self.popoverContentSize anchorRect:rect displayArea:displayArea permittedArrowDirections:arrowDirections properties:props]; + popoverArrowDirection = containerView.arrowDirection; + + UIView *keyView = self.keyView; + + backgroundView = [[WETouchableView alloc] initWithFrame:keyView.bounds]; + backgroundView.contentMode = UIViewContentModeScaleToFill; + backgroundView.autoresizingMask = ( UIViewAutoresizingFlexibleLeftMargin | + UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleRightMargin | + UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleHeight | + UIViewAutoresizingFlexibleBottomMargin); + backgroundView.backgroundColor = [UIColor clearColor]; + backgroundView.delegate = self; + + [keyView addSubview:backgroundView]; + + containerView.frame = [theView convertRect:containerView.frame toView:backgroundView]; + + [backgroundView addSubview:containerView]; + + containerView.contentView = contentViewController.view; + containerView.autoresizingMask = ( UIViewAutoresizingFlexibleLeftMargin | + UIViewAutoresizingFlexibleRightMargin); + + self.view = containerView; + [self updateBackgroundPassthroughViews]; + + if ([self forwardAppearanceMethods]) { + [contentViewController viewWillAppear:animated]; + } + [self.view becomeFirstResponder]; + popoverVisible = YES; + if (animated) { + self.view.alpha = 0.0; + + [UIView animateWithDuration:FADE_DURATION + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + + self.view.alpha = 1.0; + + } completion:^(BOOL finished) { + + [self animationDidStop:@"FadeIn" finished:[NSNumber numberWithBool:finished] context:nil]; + }]; + + } else { + if ([self forwardAppearanceMethods]) { + [contentViewController viewDidAppear:animated]; + } + } +} + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)theView + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections +{ + + [self repositionPopoverFromRect:rect + inView:theView + permittedArrowDirections:arrowDirections + animated:NO]; +} + +- (void)repositionPopoverFromRect:(CGRect)rect + inView:(UIView *)theView + permittedArrowDirections:(UIPopoverArrowDirection)arrowDirections + animated:(BOOL)animated { + + if (animated) { + [UIView beginAnimations:nil context:nil]; + [UIView setAnimationDuration:FADE_DURATION]; + [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; +// [UIView animateWithDuration:FADE_DURATION delay:0 options:UIViewAnimationOptionCurveEaseOut animations:nil completion:nil]; + } + + if (CGSizeEqualToSize(popoverContentSize, CGSizeZero)) { + popoverContentSize = contentViewController.preferredContentSize; + } + + CGRect displayArea = [self displayAreaForView:theView]; + WEPopoverContainerView *containerView = (WEPopoverContainerView *)self.view; + [containerView updatePositionWithSize:self.popoverContentSize + anchorRect:rect + displayArea:displayArea + permittedArrowDirections:arrowDirections]; + + popoverArrowDirection = containerView.arrowDirection; + containerView.frame = [theView convertRect:containerView.frame toView:backgroundView]; + + if (animated) { + [UIView commitAnimations]; + } +} + +#pragma mark - +#pragma mark WETouchableViewDelegate implementation + +- (void)viewWasTouched:(WETouchableView *)view { + if (popoverVisible) { + if (!delegate || [delegate popoverControllerShouldDismissPopover:self]) { + [self dismissPopoverAnimated:YES userInitiated:YES]; + } + } +} + +- (BOOL)isPopoverVisible { + if (!popoverVisible) { + return NO; + } + UIView *sv = self.view; + BOOL foundWindowAsSuperView = NO; + while ((sv = sv.superview) != nil) { + if ([sv isKindOfClass:[UIWindow class]]) { + foundWindowAsSuperView = YES; + break; + } + } + return foundWindowAsSuperView; +} + +@end + + +@implementation WEPopoverController(Private) + +- (UIView *)keyView { + if (self.parentView) { + return self.parentView; + } else { + UIWindow *w = [[UIApplication sharedApplication] keyWindow]; + if (w.subviews.count > 0) { + return [w.subviews objectAtIndex:0]; + } else { + return w; + } + } +} + +- (void)setView:(UIView *)v { + if (view != v) { +// [view release]; + view = v; + } +} + +- (void)updateBackgroundPassthroughViews { + backgroundView.passthroughViews = passthroughViews; +} + + +- (void)dismissPopoverAnimated:(BOOL)animated userInitiated:(BOOL)userInitiated { + if (self.view) { + if ([self forwardAppearanceMethods]) { + [contentViewController viewWillDisappear:animated]; + } + popoverVisible = NO; + [self.view resignFirstResponder]; + if (animated) { + self.view.userInteractionEnabled = NO; + + [UIView animateWithDuration:FADE_DURATION + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + + self.view.alpha = 0.0; + + } completion:^(BOOL finished) { + + [self animationDidStop:@"FadeOut" finished:[NSNumber numberWithBool:finished] context:(__bridge void *)([NSNumber numberWithBool:userInitiated])]; + }]; + + + } else { + if ([self forwardAppearanceMethods]) { + [contentViewController viewDidDisappear:animated]; + } + [self.view removeFromSuperview]; + self.view = nil; + [backgroundView removeFromSuperview]; +// [backgroundView release]; + backgroundView = nil; + } + } +} + +- (CGRect)displayAreaForView:(UIView *)theView { + CGRect displayArea = CGRectZero; + if ([theView conformsToProtocol:@protocol(WEPopoverParentView)] && [theView respondsToSelector:@selector(displayAreaForPopover)]) { + displayArea = [(id )theView displayAreaForPopover]; + } else { + UIView *keyView = [self keyView]; + displayArea = [keyView convertRect:keyView.bounds toView:theView]; + } + return displayArea; +} + +//Enable to use the simple popover style +- (WEPopoverContainerViewProperties *)defaultContainerViewProperties { + WEPopoverContainerViewProperties *ret = [WEPopoverContainerViewProperties new]; + + CGSize imageSize = CGSizeMake(30.0f, 30.0f); + NSString *bgImageName = @"popoverBgSimple.png"; + CGFloat bgMargin = 6.0; + CGFloat contentMargin = 2.0; + + ret.leftBgMargin = bgMargin; + ret.rightBgMargin = bgMargin; + ret.topBgMargin = bgMargin; + ret.bottomBgMargin = bgMargin; + ret.leftBgCapSize = imageSize.width/2; + ret.topBgCapSize = imageSize.height/2; + ret.bgImageName = bgImageName; + ret.leftContentMargin = contentMargin; + ret.rightContentMargin = contentMargin; + ret.topContentMargin = contentMargin; + ret.bottomContentMargin = contentMargin; + ret.arrowMargin = 1.0; + + ret.upArrowImageName = @"popoverArrowUpSimple.png"; + ret.downArrowImageName = @"popoverArrowDownSimple.png"; + ret.leftArrowImageName = @"popoverArrowLeftSimple.png"; + ret.rightArrowImageName = @"popoverArrowRightSimple.png"; + return ret; +} + +@end diff --git a/QRichTextView/Classes/WEPopoverParentView.h b/QRichTextView/Classes/WEPopoverParentView.h new file mode 100755 index 0000000..d881bfe --- /dev/null +++ b/QRichTextView/Classes/WEPopoverParentView.h @@ -0,0 +1,18 @@ +/* + * WEPopoverParentView.h + * WEPopover + * + * Created by Werner Altewischer on 02/09/10. + * Copyright 2010 Werner IT Consultancy. All rights reserved. + * + */ + +#import +#import + +@protocol WEPopoverParentView + +@optional +- (CGRect)displayAreaForPopover; + +@end \ No newline at end of file diff --git a/QRichTextView/Classes/WETouchableView.h b/QRichTextView/Classes/WETouchableView.h new file mode 100755 index 0000000..39432b9 --- /dev/null +++ b/QRichTextView/Classes/WETouchableView.h @@ -0,0 +1,37 @@ +// +// WETouchableView.h +// WEPopover +// +// Created by Werner Altewischer on 12/21/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import +#import + +@class WETouchableView; + +/** + * @brief delegate to receive touch events + */ +@protocol WETouchableViewDelegate + +- (void)viewWasTouched:(WETouchableView *)view; + +@end + +/** + * @brief View that can handle touch events and/or disable touch forwording to child views + */ +@interface WETouchableView : UIView { + BOOL touchForwardingDisabled; +// id delegate; + NSArray *passthroughViews; + BOOL testHits; +} + +@property (nonatomic, assign) BOOL touchForwardingDisabled; +@property (nonatomic, weak) id delegate; +@property (nonatomic, copy) NSArray *passthroughViews; + +@end diff --git a/QRichTextView/Classes/WETouchableView.m b/QRichTextView/Classes/WETouchableView.m new file mode 100755 index 0000000..2a0d49e --- /dev/null +++ b/QRichTextView/Classes/WETouchableView.m @@ -0,0 +1,70 @@ +// +// WETouchableView.m +// WEPopover +// +// Created by Werner Altewischer on 12/21/10. +// Copyright 2010 Werner IT Consultancy. All rights reserved. +// + +#import "WETouchableView.h" + +@interface WETouchableView(Private) + +- (BOOL)isPassthroughView:(UIView *)v; + +@end + +@implementation WETouchableView + +@synthesize touchForwardingDisabled, delegate, passthroughViews; + +//- (void)dealloc { +// [passthroughViews release]; +// [super dealloc]; +//} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { + if (testHits) { + return nil; + } else if (touchForwardingDisabled) { + return self; + } else { + UIView *hitView = [super hitTest:point withEvent:event]; + + if (hitView == self) { + //Test whether any of the passthrough views would handle this touch + testHits = YES; + UIView *superHitView = [self.superview hitTest:point withEvent:event]; + testHits = NO; + + if ([self isPassthroughView:superHitView]) { + hitView = superHitView; + } + } + + return hitView; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.delegate viewWasTouched:self]; +} + +@end + +@implementation WETouchableView(Private) + +- (BOOL)isPassthroughView:(UIView *)v { + + if (v == nil) { + return NO; + } + + if ([passthroughViews containsObject:v]) { + return YES; + } + + return [self isPassthroughView:v.superview]; +} + +@end