1 #import <UIKit/UIKit.h>
2 #import <QuartzCore/QuartzCore.h>
3 
4 /*
5 
6 SMCalloutView
7 -------------
8 Created by Nick Farina ([email protected])
9 Version 2.1.2
10 
11 */
12 
13 /// options for which directions the callout is allowed to "point" in.
14 typedef NS_OPTIONS(NSUInteger, SMCalloutArrowDirection) {
15     SMCalloutArrowDirectionUp = 1 << 0,
16     SMCalloutArrowDirectionDown = 1 << 1,
17     SMCalloutArrowDirectionAny = SMCalloutArrowDirectionUp | SMCalloutArrowDirectionDown
18 };
19 
20 /// options for the callout present/dismiss animation
21 typedef NS_ENUM(NSInteger, SMCalloutAnimation) {
22     /// the "bounce" animation we all know and love from @c UIAlertView
23             SMCalloutAnimationBounce,
24     /// a simple fade in or out
25             SMCalloutAnimationFade,
26     /// grow or shrink linearly, like in the iPad Calendar app
27             SMCalloutAnimationStretch
28 };
29 
30 NS_ASSUME_NONNULL_BEGIN
31 
32 /// when delaying our popup in order to scroll content into view, you can use this amount to match the
33 /// animation duration of UIScrollView when using @c -setContentOffset:animated.
34 extern NSTimeInterval const kSMCalloutViewRepositionDelayForUIScrollView;
35 
36 @protocol SMCalloutViewDelegate;
37 @class SMCalloutBackgroundView;
38 
39 //
40 // Callout view.
41 //
42 
43 #if __IPHONE_OS_VERSION_MAX_ALLOWED < 100000
44 @interface SMCalloutView : UIView
45 #else
46 @interface SMCalloutView : UIView <CAAnimationDelegate>
47 #endif
48 
49 @property (nonatomic, weak, nullable) id<SMCalloutViewDelegate> delegate;
50 /// title/titleView relationship mimics UINavigationBar.
51 @property (nonatomic, copy, nullable) NSString *title;
52 @property (nonatomic, copy, nullable) NSString *subtitle;
53 
54 /// Left accessory view for the call out
55 @property (nonatomic, strong, nullable) UIView *leftAccessoryView;
56 /// Right accessoty view for the call out
57 @property (nonatomic, strong, nullable) UIView *rightAccessoryView;
58 /// Default @c SMCalloutArrowDirectionDown
59 @property (nonatomic, assign) SMCalloutArrowDirection permittedArrowDirection;
60 /// The current arrow direction
61 @property (nonatomic, readonly) SMCalloutArrowDirection currentArrowDirection;
62 /// if the @c UIView you're constraining to has portions that are overlapped by nav bar, tab bar, etc. you'll need to tell us those insets.
63 @property (nonatomic, assign) UIEdgeInsets constrainedInsets;
64 /// default is @c SMCalloutMaskedBackgroundView, or @c SMCalloutDrawnBackgroundView when using @c SMClassicCalloutView
65 @property (nonatomic, strong) SMCalloutBackgroundView *backgroundView;
66 
67 /**
68  @brief Custom title view.
69 
70  @disucssion Keep in mind that @c SMCalloutView calls @c -sizeThatFits on titleView/subtitleView if defined, so your view
71  may be resized as a result of that (especially if you're using @c UILabel/UITextField). You may want to subclass and override @c -sizeThatFits, or just wrap your view in a "generic" @c UIView if you do not want it to be auto-sized.
72 
73  @warning If this is set, the respective @c title property will be ignored.
74  */
75 @property (nonatomic, strong, nullable) UIView *titleView;
76 
77 /**
78  @brief Custom subtitle view.
79 
80  @discussion Keep in mind that @c SMCalloutView calls @c -sizeThatFits on subtitleView if defined, so your view
81  may be resized as a result of that (especially if you're using @c UILabel/UITextField). You may want to subclass and override @c -sizeThatFits, or just wrap your view in a "generic" @c UIView if you do not want it to be auto-sized.
82 
83  @warning If this is set, the respective @c subtitle property will be ignored.
84  */
85 @property (nonatomic, strong, nullable) UIView *subtitleView;
86 
87 /// Custom "content" view that can be any width/height. If this is set, title/subtitle/titleView/subtitleView are all ignored.
88 @property (nonatomic, retain, nullable) UIView *contentView;
89 
90 /// Custom content view margin
91 @property (nonatomic, assign) UIEdgeInsets contentViewInset;
92 
93 /// calloutOffset is the offset in screen points from the top-middle of the target view, where the anchor of the callout should be shown.
94 @property (nonatomic, assign) CGPoint calloutOffset;
95 
96 /// default SMCalloutAnimationBounce, SMCalloutAnimationFade respectively
97 @property (nonatomic, assign) SMCalloutAnimation presentAnimation, dismissAnimation;
98 
99 /// Returns a new instance of SMCalloutView if running on iOS 7 or better, otherwise a new instance of SMClassicCalloutView if available.
100 + (SMCalloutView *)platformCalloutView;
101 
102 /**
103  @brief Presents a callout view by adding it to "inView" and pointing at the given rect of inView's bounds.
104 
105  @discussion Constrains the callout to the bounds of the given view. Optionally scrolls the given rect into view (plus margins)
106  if @c -delegate is set and responds to @c -delayForRepositionWithSize.
107 
108  @param rect @c CGRect to present the view from
109  @param view view to 'constrain' the @c constrainedView to
110  @param constrainedView @c UIView to be constrainted in @c view
111  @param animated @c BOOL if presentation should be animated
112  */
113 - (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
114 
115 /**
116  @brief Present a callout layer in the `layer` and pointing at the given rect of the `layer` bounds
117 
118  @discussion Same as the view-based presentation, but inserts the callout into a CALayer hierarchy instead.
119  @note Be aware that you'll have to direct your own touches to any accessory views, since CALayer doesn't relay touch events.
120 
121  @param rect @c CGRect to present the view from
122  @param layer layer to 'constrain' the @c constrainedLayer to
123  @param constrainedLayer @c CALayer to be constrained in @c layer
124  @param animated @c BOOL if presentation should be animated
125  */
126 - (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer constrainedToLayer:(CALayer *)constrainedLayer animated:(BOOL)animated;
127 
128 /**
129  Dismiss the callout view
130 
131  @param animated @c BOOL if dismissal should be animated
132  */
133 - (void)dismissCalloutAnimated:(BOOL)animated;
134 
135 /// For subclassers. You can override this method to provide your own custom animation for presenting/dismissing the callout.
136 - (CAAnimation *)animationWithType:(SMCalloutAnimation)type presenting:(BOOL)presenting;
137 
138 @end
139 
140 //
141 // Background view - default draws the iOS 7 system background style (translucent white with rounded arrow).
142 //
143 
144 /// Abstract base class
145 @interface SMCalloutBackgroundView : UIView
146 /// indicates where the tip of the arrow should be drawn, as a pixel offset
147 @property (nonatomic, assign) CGPoint arrowPoint;
148 /// will be set by the callout when the callout is in a highlighted state
149 @property (nonatomic, assign) BOOL highlighted;
150 /// returns an optional layer whose contents should mask the callout view's contents (not honored by @c SMClassicCalloutView )
151 @property (nonatomic, assign) CALayer *contentMask;
152 /// height of the callout "arrow"
153 @property (nonatomic, assign) CGFloat anchorHeight;
154 /// the smallest possible distance from the edge of our control to the "tip" of the anchor, from either left or right
155 @property (nonatomic, assign) CGFloat anchorMargin;
156 @end
157 
158 /// Default for iOS 7, this reproduces the "masked" behavior of the iOS 7-style callout view.
159 /// Accessories are masked by the shape of the callout (including the arrow itself).
160 @interface SMCalloutMaskedBackgroundView : SMCalloutBackgroundView
161 @end
162 
163 //
164 // Delegate methods
165 //
166 
167 @protocol SMCalloutViewDelegate <NSObject>
168 @optional
169 
170 /// Controls whether the callout "highlights" when pressed. default YES. You must also respond to @c -calloutViewClicked below.
171 /// Not honored by @c SMClassicCalloutView.
172 - (BOOL)calloutViewShouldHighlight:(SMCalloutView *)calloutView;
173 
174 /// Called when the callout view is clicked. Not honored by @c SMClassicCalloutView.
175 - (void)calloutViewClicked:(SMCalloutView *)calloutView;
176 
177 /**
178  Called when the callout view detects that it will be outside the constrained view when it appears,
179  or if the target rect was already outside the constrained view. You can implement this selector
180  to respond to this situation by repositioning your content first in order to make everything visible.
181  The @c CGSize passed is the calculated offset necessary to make everything visible (plus a nice margin).
182  It expects you to return the amount of time you need to reposition things so the popup can be delayed.
183  Typically you would return @c kSMCalloutViewRepositionDelayForUIScrollView if you're repositioning by calling @c [UIScrollView @c setContentOffset:animated:].
184 
185  @param calloutView the @c SMCalloutView to reposition
186  @param offset calculated offset necessary to make everything visible
187  @returns @c NSTimeInterval to delay the repositioning
188  */
189 - (NSTimeInterval)calloutView:(SMCalloutView *)calloutView delayForRepositionWithSize:(CGSize)offset;
190 
191 /// Called before the callout view appears on screen, or before the appearance animation will start.
192 - (void)calloutViewWillAppear:(SMCalloutView *)calloutView;
193 
194 /// Called after the callout view appears on screen, or after the appearance animation is complete.
195 - (void)calloutViewDidAppear:(SMCalloutView *)calloutView;
196 
197 /// Called before the callout view is removed from the screen, or before the disappearance animation is complete.
198 - (void)calloutViewWillDisappear:(SMCalloutView *)calloutView;
199 
200 /// Called after the callout view is removed from the screen, or after the disappearance animation is complete.
201 - (void)calloutViewDidDisappear:(SMCalloutView *)calloutView;
202 
203 NS_ASSUME_NONNULL_END
204 @end
205