How to animate along with device rotation on iOS 7 - animation

I want to do some animation along with device rotation. On iOS 8, I can do this:
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// My custom animation
} completion:nil];
}
But this method is not available on iOS 7. I tried to do the similar thing as follows:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
// Custom animations
} completion:nil];
}
But the self.transitionCoordinator object is nil here. Does anyone know how to achieve the same effect on iOS 7?
I'm not sure if I just call this animateAlongsideTransition method in the wrong place or even if animateAlongsideTransition is the right method to call. So any help/guidance would be appreciated.
Thanks

It looks like I have found an answer for my own question. For those who are also wondering the same thing, willAnimateRotationToInterfaceOrientation method is the one to use.
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[UIView animateWithDuration:duration animations:^{
// Custom animations
}];
}
And it looks like the custom animation has to be here. If I move the animation block to the willRotateToInterfaceOrientation method, it won't work.

Related

Detecting UIScrollView Motion

My UIScrollView is moving using this code:
[self.scrollView setContentOffset:newOffset animated:YES];
And it will trigger
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Multiple times, and that OK. However, I am seeking a way to query the scroll view and know whether it has reached its' final station, or it is still in motion. Is there a way to do that? isTracking and decelerating are returning NO.
Thank you..!
Make sure your controller conforms to <UIScrollViewDelegate>...
and implement:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
From Apple's docs:
Discussion
The scroll view calls this method at the end of its implementations of the setContentOffset(_:animated:) and scrollRectToVisible(_:animated:) methods, but only if animations are requested.
add a BOOL property:
#property (assign, readwrite) BOOL bIsScrolling;
then you can do this:
self.bIsScrolling = YES;
[self.scrollView setContentOffset:newOffset animated:YES];
and this:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
NSLog(#"done scrolling");
self.bIsScrolling = NO;
}
and elsewhere you can check if (bIsScrolling) ...

Detecting panning + decelerate of MKMapView

I'm trying to capture panning and the 'end of scrolling' on an MKMapView. Panning is easy to do with a gesture recognizer. However, MKMapView doesn't seem to implement a UIScrollViewDelegate in iOS 6. That makes the solution in Is there way to limit MKMapView maximum zoom level? not work.
Thoughts? Ideally I would have just leveraged the UIScrollViewDelegate as such:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if ([super respondsToSelector:#selector(scrollViewDidEndDecelerating:)]) {
[super scrollViewDidEndDecelerating:scrollView];
}
[self.myDelegate mapDidFinishPanning:self];
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate: (BOOL)decelerate {
if ([super respondsToSelector:#selector(scrollViewDidEndDragging:)]) {
[super scrollViewDidEndDragging:scrollView];
}
if(!decelerate) {
[self.myDelegate mapDidFinishPanning:self];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if ([super respondsToSelector:#selector(scrollViewWillBeginDragging:)]) {
[super scrollViewWillBeginDragging:scrollView];
}
[self.myDelegate mapDidBeginPanning:self];
}
inside a class extending MKMapView
#interface MyMapView : MKMapView <UIScrollViewDelegate, UIGestureRecognizerDelegate>
but that won't work in iOS 6. I can't see anything sufficient in MKMapViewDelegate.
Answering myself. I implemented all of MKMapViewDelegate's methods and it seems that
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
is called on pan, and
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
is called as soon as deceleration stops (and not before).

Remove elasticity from webview

I have a webview which I would like to remove the elasticity from. As it is now, when scrolling a page that is smaller than the webview, it will make an elasticity effect revealing the background underneath. I would like to remove this.
I have tried doing the following but without success. It finds the WebDynamicScrollBarsView but setting the elasticity of this, does not change anything.
- (void)awakeFromNib
{
NSScrollView *scrollView = [self findScrollViewInSubviews:self.subviews];
scrollView.horizontalScrollElasticity = NSScrollElasticityNone;
scrollView.verticalScrollElasticity = NSScrollElasticityNone;
}
- (NSScrollView *)findScrollViewInSubviews:(NSArray *)subviews
{
for (NSView *view in subviews)
{
if ([view isKindOfClass:[NSScrollView class]])
return (NSScrollView *) view;
else
return [self findScrollViewInSubviews:view.subviews];
}
return nil;
}
Does anyone know how to remove the elasticity effect from a webview?
In my subclass of WebView (or you can just do it directly, with webView.mainFrame.frameView.documentView.enclosingScrollView, but this makes it easier for me, personally)
// return the scroll view that we are currently using, if applicatble
- (NSScrollView *)mainScrollView {
return [[[[self mainFrame] frameView] documentView] enclosingScrollView]; // can be nil
}
Then on finish load, as Anne noted literally as I was typing my answer :) ,
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSScrollView *mainScrollView = [sender mainScrollView];
[mainScrollView setVerticalScrollElasticity:NSScrollElasticityNone];
[mainScrollView setHorizontalScrollElasticity:NSScrollElasticityNone];
}
I'm not sure if doing it after the content has loaded is necessary, but I don't have my mac with me to try right now.

How to change color of divider in NSSplitView?

Can we change the color of the divider?
Apple documentations says, that we can override -dividerColor in subclass of NSSplitView for this, but it doesn't works for me, or my understanding isn't correct. Also I've try create color layer over divider, e.g.:
colorLayer = [CALayer layer];
NSRect dividerFrame = NSMakeRect([[self.subviews objectAtIndex:0] frame].size.width, [[self.subviews objectAtIndex:0] frame].origin.y, [self dividerThickness], self.frame.size.height);
[colorLayer setBackgroundColor:[color coreGraphicsColorWithAlfa:1]];
[colorLayer setFrame:NSRectToCGRect(dividerFrame)];
[self.layer addSublayer:colorLayer];
Not works.
This answer may be late but:
If you are using Interface Builder, it is possible to change the property by going to the Identity Inspector of the NSSplitView (cmd+alt+3) and adding a User Defined Runtime Attribute for dividerColor of the type Color.
Actually, simply subclassing NSSplitView and overriding -(void)dividerColor works, but works only for thin or thick divider.
I've created simple configurable split view like this:
#interface CustomSplitView : NSSplitView
#property NSColor* DividerColor
#end
#implementation CustomSplitView
- (NSColor*)dividerColor {
return (self.DividerColor == nil) ? [super dividerColor] : self.DividerColor;
}
#end
Then in Interface Builder specify custom class for your split view to be CustomSplitView and add new user defined runtime attribute with key path = DividerColor, type = Color and select desired splitter color.
I've tried subclassing - (void)dividerColor too and I'm not sure why it doesn't work even though I know it's being called (and it's in the documentation).
One way to change the color of the divider is to subclass - (void)drawDividerInRect:(NSRect)aRect. However, for some reason, this method isn't called and I've checked all over the web for answers, but couldn't find anything, so I ended up calling it from drawRect. Here is the code for the subclassed NSSplitView:
-(void) drawRect {
id topView = [[self subviews] objectAtIndex:0];
NSRect topViewFrameRect = [topView frame];
[self drawDividerInRect:NSMakeRect(topViewFrameRect.origin.x, topViewFrameRect.size.height, topViewFrameRect.size.width, [self dividerThickness] )];
}
-(void) drawDividerInRect:(NSRect)aRect {
[[NSColor redColor] set];
NSRectFill(aRect);
}
Based on Palle's answer, but with the possibility to change the color dynamically in code, I'm currently using this solution (Swift 4):
splitView.setValue(NSColor.red, forKey: "dividerColor")
If your splitview control is part of a NSSplitViewController, you should use something like this:
splitViewController?.splitView.setValue(NSColor.red, forKey: "dividerColor")
In Swift and on macOS 11 I was able to achieve this by simply subclassing the NSSPlitView and only override drawDivider()
import Foundation
import AppKit
class MainSplitView: NSSplitView {
override func drawDivider(in rect: NSRect) {
NSColor(named: "separatorLinesColor")?.setFill()
rect.fill()
}
}
I had previously tried some of the other way, listed in here and what used to work stopped working with macOS 11... but it seems that this works.
One important point I haven't seen mentioned anywhere is that if you are overriding drawRect in a split view then you must call super -- otherwise drawDividerInRect: is never called. So, it should go something like this:
- (void)drawRect:(NSRect)dirtyRect {
// your other custom drawing
// call super last to draw the divider on top
[super drawRect:dirtyRect];
}
- (void)drawDividerInRect:(NSRect)aRect {
[[NSColor blueColor] set];
NSRectFill(aRect);
}

WebView - User has scrolled

I cannot figure out how to have a WebView in Objective-c detect when a scroll has been made. I have looked at the WebFrameLoadDelegate: and found didChangeLocationWithinPageForFrame: method, but that did seem to work.
You'll want to detect the webview is scrolling by using javascript. If you do a quick google search on "uiwebview javascript" you'll see plenty of examples on how to have javascript run in the uiwebivew. Once you get the javascript to detect the scroll occurring then you have the javascript change window.location to something fake and implement the "webView:shouldStartLoadWithRequest:navigationType:" delegate to execute objective-c code. Return NO from the delegate method to not load the request.
Depends on whether you are using a UIWebView (iOS - Cocoa Touch) or WebView (OS X - Cocoa).
iOS (iOS 5 and later):
UIWebView exposes its UIScrollView, and you can set the scroll view's delegate and then implement the delegate scrollViewDidScroll: delegate method (adding to your class's #interface declaration first, of course; this example is in a UIViewController subclass):
- (void)viewDidLoad {
[super viewDidLoad];
[_webView.scrollView setDelegate:self];
}
#pragma mark UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// do something in response to scroll
}
}
OS X:
Add an observer for the NSViewBoundsDidChangeNotification of the WebView (this example is in an NSWindowController subclass):
- (id)initWithWindowNibName:(NSString *)windowNibName {
self = [super initWithWindowNibName:windowNibName];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(_scrollDetected)
name:NSViewBoundsDidChangeNotification
object:_webView];
}
return self;
}
- (void)_scrollDetected {
// do something in response to scroll
}
On OS X, you can detect it by subscribing to NSScrollViewWillStartLiveScrollNotification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mySelector:)
name:NSScrollViewWillStartLiveScrollNotification object:nil];
I pass nil as the object parameter because when I get it, it doesn't appear to actually come from the enclosingScrollView on the WebView. And there is no scroll view property on WKWebView in Yosemite. So when handling it, you have to check if it's your web view sending it (being paranoid about type safety):
-(void)handleScroll:(id)sender
{
if ([sender isKindOfClass:[NSNotification class]])
{
NSNotification *notif = (NSNotification *)sender;
if ([notif.object isKindOfClass:[NSView class]])
{
NSView *view = (NSView *)notif.object;
if ([view isDescendantOf:self.webView])
{
//Handle scroll here
}
}
}
}
I have only tried this descendent-checking thing with WebView, so if you're using WKWebView, YMMV.
There are other scroll notifications listed in the NSScrollView documentation.

Resources