How to dynamically move UIView with autolayout

Leave a comment

Here we see an example of how to move an entire UIView with auto layout.
The idea here is to:

  1. Create a UIView
  2. Add some controls to it
  3. Make something happen.
  4. And then move the entire view by adjusting the value of a constraint.

First create a new single page application and drag a UIView onto it.

Screen Shot 2016-09-28 at 7.30.02 AM.png

Give it a background colour of green. Stretch it out so it locks to the sides of the parent view.

Then add have autolayout automatically add some constraints to it by clicking

Screen Shot 2016-09-28 at 7.37.57 AM.png

Screen Shot 2016-09-28 at 7.38.07 AM.png

This will automatically add the necessary constrains for us. Including the top one which we want to manipulate.

Then drag the top constraint over as an outlet, and add two buttons to reset it’s size programatically.

The buttons are going to reside inside our new UIView

Screen Shot 2016-09-28 at 7.39.59 AM.png

We are going to slide the whole view up/down by pressing the buttons and manipulating the value of the top constraints.

Add constraints to the Up/Down buttons so they stay embedded in the UIView like so

Screen Shot 2016-09-28 at 7.40.55 AM.png

Screen Shot 2016-09-28 at 7.41.26 AM.png

Screen Shot 2016-09-28 at 7.41.36 AM.png

Screen Shot 2016-09-28 at 7.42.03 AM.png

Your constraints should now look like this.

Screen Shot 2016-09-28 at 7.42.20 AM.png

Now the fun part. Add code to slide the entire UIView up and down.

Do this by adding actions for the up/down buttons and the following code.

If you want to get fancy you can add an animation like in the down button.

All the code and everything should eventually look like this.

Screen Shot 2016-09-28 at 7.50.09 AM.png

 

#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) IBOutlet UIView *baseView;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    CGRect newFrame = self.baseView.frame;

    newFrame.size.width = [[UIScreen mainScreen] bounds].size.width;
    newFrame.size.height = [[UIScreen mainScreen] bounds].size.height;
    [self.baseView setFrame:newFrame];

    self.baseView.userInteractionEnabled = YES;
}

- (IBAction)upPressed:(UIButton *)sender {
    NSLog(@"Up pressed");
    self.topConstraint.constant = 0;
    [self.view layoutIfNeeded];
}

- (IBAction)downPressed:(UIButton *)sender {
    NSLog(@"Down pressed");

    [UIView animateWithDuration:0.5
                     animations:^{
                         self.topConstraint.constant = 100;
                         [self.view layoutIfNeeded];
                     }];
}

@end

And when you run it and press the up/down buttons, the whole thing should like up/down.

Screen Shot 2016-09-28 at 7.51.34 AM.png

Screen Shot 2016-09-28 at 7.51.39 AM.png

Advertisement

How to dynamically move an object in autolayout

1 Comment

Layout a button with some constraints like this.

screen-shot-2016-09-28-at-6-53-18-am

Create an outlet of the constraint you want to manipulate.

Then add a button, so that when you press it, you can adjust the value of the constraint so it moves.

screen-shot-2016-09-28-at-6-58-02-am

Now when you press the move button, the constraint value will change and the button will move.

Now if you want to get really fancy, you can animate the movement like this.

 

- (IBAction)movePressed:(UIButton *)sender {

    [UIView animateWithDuration:0.5
                     animations:^{
                         self.topConstraint.constant = 100;
                         [self.view layoutIfNeeded];
                     }];
}

Objective-C Unit Testing Tricks

Leave a comment

Getting to an objects innards through categories

Normally you only want to test an object entirely through it’s public API. But for those times when you need to access an objects innards (but would or can’t expose them), you can make them public by defining the inner method as a category in your unit test.

- (void)testAddNumberToStack {
    Calculator *calc = [Calculator new];
    [calc pushOperand:1.0];
    XCTAssertEqual(1, calc.stackCount); // private
}

Calculator.m

-(NSUInteger)stackCount {
    return [self.myStack count]; // private
}

CalculatorTest.m

@interface Calculator (Tests) // category
-(NSUInteger)stackCount;
@end

Alternative to partical mocks

For those times when you want to override a method in a class under test, declare a new class and override the method in your test.

@interface TestSubclass : ClassUnderTest
@end

@implementation TestSubclass

- (void)methodToOverride
{
    ...
}

...
// In the test
- (void)testSubclass
{
    ClassUnderTest *testObject = [TestSubclass new];

    XCTAssert(...);
}

@end

Objective-C – How to stub out an NSError unit test

Leave a comment

#import <XCTest/XCTest.h>

#import "Player.h"

// Mocking somethings (like NSError's) can be tricky in OCMockito.
// One alternative, if your mocks get too complex, is to simply manually stub.

// create a test class for the behaviour your want to stub,
// override the methods
// and then inject that into your SUT

// For example

// Test class we want to stub
@interface FakeHandler : Handler
- (BOOL)playTrack:(NSString *)track error:(NSError **)errorPtr;
- (BOOL)playAlbum:(NSString *)album error:(NSError **)errorPtr;
@end

@implementation FakeHandler

// Method / behaviour we want to override
- (BOOL)playTrack:(NSString *)track error:(NSError **)errorPtr {

    NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
    NSString *desc = NSLocalizedString(@"Unable to connect to network.", @"");
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };

    *errorPtr = [NSError errorWithDomain:domain
                                         code:-101
                                     userInfo:userInfo];

    return NO;
}

- (BOOL)playAlbum:(NSString *)album error:(NSError **)errorPtr {
    return NO;
}

@end


// Now we can use all of this in our test.

@interface PlayerStubTest : XCTestCase
@property (nonatomic, strong) Player *player;
@property (nonatomic, strong) Handler *fakeHandler;
@end

@implementation PlayerStubTest

- (void)setUp {
    [super setUp];
    self.fakeHandler = [FakeHandler new];
    self.player = [[Player alloc] initWithHandler:self.fakeHandler]; // Dependence inject our stub
}

- (void)testNetworkFailure {
    // Now when we call our play method, we can expect a failure and test that it bubbles up
    NSError *expectedError;
    [self.player playURL:@"track.xxx" error:&expectedError];
    XCTAssertNotNil(expectedError);
}

@end

Dealing with Errors in Objective-C

1 Comment

A reminder to myself around where and when to use NSError vs throwing exception in Objective-C.

This article gives a nice summary

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html

Use NSError for Most errors

When things go wrong in your app, due to

x network connectivity
x database been down etc

Use NSError and pass errors by reference.

- (BOOL)writeToURL:(NSURL *)aURL
 options:(NSDataWritingOptions)mask
 error:(NSError **)errorPtr;

Create an error pointer/reference and pass it to your method

NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL options:0
 error:&anyError];

if (!success) {
 NSLog(@"Write failed with error: %@", anyError);
 // present error to user
}

The recover if possible and display an error to the user.

Generating your own errors

To create your own error define your own error domain.

com.companyName.appOrFrameworkName.ErrorDomain

Along with a unique error code for each error that may occur.

NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
 NSString *desc = NSLocalizedString(@"Unable to…", @"");
 NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
 
 NSError *error = [NSError errorWithDomain:domain
 code:-101
 userInfo:userInfo];

Then use as follows

- (BOOL)doSomethingThatMayGenerateAnError:(NSError **)errorPtr;

- (BOOL)doSomethingThatMayGenerateAnError:(NSError **)errorPtr {
 ...
 // error occurred
 if (errorPtr) {
 *errorPtr = [NSError errorWithDomain:...
 code:...
 userInfo:...];
 }
 return NO;
}

Exceptions are Used for Programmer Errors

- (void)testInvalidUrl {
 XCTAssertThrows([self.player playURL:@"foobar" error:NULL], @"Invlaid url.");
}

- (BOOL)playURL:(NSString *)url error:(NSError **)errorPtr {
 if ([url containsString:@"track"]) {
 return [self.handler playTrack:url error:errorPtr];
 } else if ([url containsString:@"album"]) {
 return [self.handler playAlbum:url error:errorPtr];
 } else {
 [NSException raise:@"Invalid url" format:@"%@ must be a track or an album.", url];
 }
 return NO;
}

Objective-C initializer unavailable

Leave a comment

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
#import 

@interface Calculator : NSObject

@property (nonatomic) double result;

- (void)pushOperand:(double)operand;
- (void)add;

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

@end

Objective-C init style

Leave a comment

Currently using this for default object initialization in Objective-C

- (instancetype)init
{
    self = [super init];
    if (self) {
        // do init work here, e.g. _myIvar = someParam;
    }
    return self;
}

For example

Player.h

@interface Player : NSObject
- (instancetype)initWithHandler:(Handler *)handler;
@end

Player.m

- (instancetype)initWithHandler:(Handler *)handler
{
    self = [super init];
    if (self) {
        _handler = handler;
    }
    return self;
}

Autolayout – how to hang single button

Leave a comment

Drag out your buttonScreen Shot 2016-09-12 at 8.19.20 AM.png

Vertical Spacing to Top

Screen Shot 2016-09-12 at 8.19.53 AM.png

So now looks like

Screen Shot 2016-09-12 at 8.20.01 AM.png

Center horizontally by clicking Align button at bottom

Screen Shot 2016-09-12 at 8.22.16 AM.png

Screen Shot 2016-09-12 at 8.22.44 AM.png

Screen Shot 2016-09-12 at 8.23.03 AM.png

Run simulator should now get.Screen Shot 2016-09-12 at 8.23.32 AM.png

To hang a centered button underneath that drag out another button.

Screen Shot 2016-09-12 at 8.26.56 AM.png

Control drag from the bottom button to the top selecting Center Horizontally.

Screen Shot 2016-09-12 at 8.27.47 AM.png

Screen Shot 2016-09-12 at 8.29.39 AM.png

Then do the same control drag from the bottom button to the top again yet this time select Vertical Spacing.

Screen Shot 2016-09-12 at 8.30.07 AM.png

Screen Shot 2016-09-12 at 8.31.17 AM.png

Run simulator

Screen Shot 2016-09-12 at 8.31.42 AM.png

 

 

 

 

AppCode Formatting

Leave a comment

When you go Alt + Option + Command you can instantly reformat all your code in AppCode. But sometimes you want to adjust the layout to meet your teams coding standard.

Here is a list of tweaks I have had to make and where to find them in AppCode.

Adding a space after a property

Screen Shot 2016-04-11 at 8.22.08 AM.png

Ones I haven’t figured out yet

There should be no space between id and Protocol here.

@property(nonatomic, strong) id&lt;PlayerAPI&gt; playerAPI;
(void)playerStateDidChange:(id&lt;PlayerState&gt;)playerState

Space after const in variable

ConnectionParams * const connectionParams =

Objective-C Object Initialization

Leave a comment

Here are some guidelines and macros for using designated initializers in objective-c.

Basically you pick your own initializer as the designated one. And the assumption is that it will call it’s parent when initializing.

If you have many convenience initializers, they should all come down to one main initializer. You can make that one up with the NS_DESIGNATED_INITIALIZER macro also.

Header.h

@interface Foo : NSObject

/**
* Initializes a new user API.
*
* @discussion This is the designated initializer.
*
* @param client A client responsible for sending and receiving messaging to Spotify app.
*/
- (instancetype)initWithClient:(Client *)client NS_DESIGNATED_INITIALIZER;

#pragma mark Unavailable Initializers

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

Implementation.m

@implementation SPTAppRemoteUserAPIImplementation

- (instancetype)initWithClient:(Client *)client
{
   self = [super init];
   if (self) {
       _client  = client;
   }
   return self;
}

Older Entries

%d bloggers like this: