UIPageViewController that has UIPageControl with different dot colours

 Customise page control

  1. Create page control subclassing UIView
  2. Draw the coloured dots in - (void) drawRect:(CGRect) rect
  3. Control the touch events using - (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
  4. Inform page view controller when user touch it using delegation.

Customise page view controller

  1. Override - (void) viewDidLayoutSubviews and search all the subviews and find the UIPageControl instance.
  2. Hide the UIPageControl instance and add the custom page control instance to its superview.
  3. Set the UIPageControl instance frame to custom page control.
  4. Bring the custom page control using [self.view bringSubviewToFront:pageControl];
  5. When custom page control informs you about the user touch then call the method - (void)setViewControllers:(nullable NSArray *)viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^ __nullable)(BOOL finished))completion; to move to right or left page.

Page view controller delegate

  1. Update custom control page dots/index using
    - (void) pageViewController:(COPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *) previousViewControllers transitionCompleted:(BOOL) completed
  2. This method - (void) pageViewController:(COPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *) pendingViewControllers will start when user swipes the page. What I did is maintained a variable that tells whether the page is still in transition, based on that I will allow the user interaction with custom page control.

 

Advertisements

Understand NSAssert (and its stdlib equivalent Assert)

Assert is to make sure a value is what its supposed to be. If an assertion fails that means something went wrong and so the app quits.

It is important to make sure that there is no data loss in any such situation.

Note that XCode 4 has NS_BLOCK_ASSERTIONS defined by default in release configurations. I guess if you don’t change that your released code will not contain NSAsserts. So, just put the macro in your distribution target [only].

NSAssert (and its stdlib equivalent Assert) can be very useful for debugging/unit testing, and also when you provide frameworks to stop the users from doing “evil” things. So not only does it safe-guard against potentially bad inputs but it logs them in a useful, standard way.

Assertions are commonly used to enforce the intended use of a particular method or piece of logic. They enforce that your code is used only as intended.

The value that’s passed is entered by the user, you need to do proper validation of the input rather than relying on the assertion. A user entering a value that is not allowed should be followed by a UI error, not NSAssert crashing the application.

NSAssert is to throw an exception, which you can catch and handle (try/catch).

KVO crash NSKVODeallocate

This crash happens when an instance gets released before all key value observers pointing to its properties release. You need to call removeObserver:forKeyPath: before releasing the instance.

In another scenario, it will crash if you call removeObserver:forKeyPath: on an instance if none of the key value observers are pointing to its properties.

Convert iPad xib to iPhone version

We can go with the following approach to get iPhone version of iPad xibs already exist in the project. With this approach we no need to create a new xib and manually add all the subviews.

1. Xcode recognises the iPhone version of a XIB if the suffix is ~iphone (lowercase only) and iPad version if its ~ipad. If no suffix exists then it will consider it as an iPhone version. Lets suffix all the existing xib names with ~ipad.

2. Duplicate all the xib files and open them in TextEditor and do the following.

change “com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB” to “com.apple.InterfaceBuilder3.CocoaTouch.XIB”
change “iOS.CocoaTouch.iPad” to “iOS.CocoaTouch”
change iPad size (width=”1024″ height=“768”) to iPhone size (width=“320″ height=“568″)

3. Add the xib to the project and open it on Xcode.

4. Select the root view and go to the attributes inspector.

5. Change the orientation to portrait and size to Retina 4-inch Full Screen.

6. Adjust the frame of all the subviews.

Make vertical scrollbar always visible in UIScrollView

Download : https://github.com/jaredsinclair/JTSScrollIndicator

JTSScrollIndicator.m

Add the following statements in the method initWithScrollView:

  • [_scrollView setShowsHorizontalScrollIndicator:NO];
  • [_scrollView setShowsVerticalScrollIndicator:NO];

Comment all the statements in the method hide:

JTSScrollIndicator.h

Added the following method declarations

  • – (void)show:(BOOL)animated;
  • + (BOOL)indicatorShouldBeVisibleForScrollView:(UIScrollView *)scrollView;

ViewController.h

#import “JTSScrollIndicator.h”
@property (strong, nonatomic) JTSScrollIndicator *indicator;

ViewController.m

- (void)viewDidLoad {
[super viewDidLoad];
self.indicator = [[JTSScrollIndicator alloc] initWithScrollView:scrollview];
self.indicator.backgroundColor = [UIColor colorWithRed:5.0/255.0 green:127.0/255.0 blue:231.0/255.0 alpha:1.0];
}

- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
scrollview.contentOffset = CGPointMake(0.0, 1.0);
[self.indicator show:YES];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self.indicator scrollViewDidScroll:scrollView];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[self.indicator scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self.indicator scrollViewDidEndDecelerating:scrollView];
}

- (void)scrollViewWillScrollToTop:(UIScrollView *)scrollView {
[self.indicator scrollViewWillScrollToTop:scrollView];
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
[self.indicator scrollViewDidScrollToTop:scrollView];
}

Objective-C – How to use member variables in the categories.

.h

@interface UIView (ObjectTagAdditions)
@property (nonatomic, retain) id objectTag;
@end

.m

#import <objc/runtime.h>
static char const * const ObjectTagKey = "ObjectTag";

@implementation UIView (ObjectTagAdditions)
@dynamic objectTag;

- (id)objectTag {
    return objc_getAssociatedObject(self, &ObjectTagKey);
}

- (void)setObjectTag:(id)newObjectTag {
    objc_setAssociatedObject(self, &ObjectTagKey, newObjectTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

Cited from : http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/

Convert yyyy-MM-ddTHH:mm:ss.SSSZ format to default timezone

– (NSDate *) localDateStringForISODateTimeString:(NSString *) anISOString

{
NSDateFormatter *isoDateFormatter = [[NSDateFormatter alloc] init];
[isoDateFormatter setDateFormat:@”yyyy-MM-dd’T’HH:mm:ss.SSS’Z'”];
[isoDateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@”UTC”]];

NSDateFormatter *userFormatter = [[NSDateFormatter alloc] init];
[userFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@”UTC”]];

NSDate *date = [isoDateFormatter dateFromString:anISOString];
return [dateFormatter dateFromString:[userFormatter stringFromDate:date]];
}