Symantec Connect – 100 Solutions

After 6 years and 23 weeks I’ve hit 100 solutions from 1,028 comments.

Not sure if that’s a good ratio in comparison to others but it is a milestone I thought I’d document.

You can find my profile here:

https://www.symantec.com/connect/user/alexhedley

I’ve made a lot of Articles over the years, mostly for Symantec Workflow / ServiceDesk / SMP.

Let’s see when I hit 200!

JSQMessagesViewController – Messages UI

On the app I’m currently working on I’ve got a messaging portion.

Originally I implemented a basic TableView with a TextField to add a message.

Then I searched for Messages UI frameworks and came across JSQMessagesViewController

There’s a Sample project you can look into – JSQMessagesDemo.

It comes as a Cocoapod:

I’m using 7.3.4.


Create a new Xcode project.

Open terminal at that folder

pod init

add

pod 'JSQMessagesViewController'

then

pod install

Open the Xcode ‘#App.xcworkspace’

Create a View Controller that inherits

: JSQMessagesViewController<UIActionSheetDelegate, JSQMessagesComposerTextViewPasteDelegate>
#import <JSQMessagesViewController/JSQMessagesViewController.h>
#import "DemoData.h"
@class MessageViewController;
@protocol MessageViewControllerDelegate <NSObject>
- (void)didDismissMessagesViewController:(MessageViewController *)vc;
@end

Add some properties and methods

@property (weak, nonatomic) id<MessageViewControllerDelegate> delegateModal;
@property (strong, nonatomic) DemoData *data;
- (void)receiveMessagePressed:(UIBarButtonItem *)sender;
- (void)closePressed:(UIBarButtonItem *)sender;

I didn’t bother with any of the settings so commented these checks out.


I couldn’t find the Accessory Button Delegate to add to my View Controller.

#import "JSQMessagesViewAccessoryButtonDelegate.h"
<JSQMessagesViewAccessoryButtonDelegate>

For testing create a new class for your demo data.

DemoData.h

@interface DemoData : NSObject
@property (strong, nonatomic) NSMutableArray *messages;
@property (strong, nonatomic) NSDictionary *users;
@property (strong, nonatomic) JSQMessagesBubbleImage *outgoingBubbleImageData;
@property (strong, nonatomic) JSQMessagesBubbleImage *incomingBubbleImageData;
@end


DemoData.m

Create some test Users.

- (instancetype)init
{
    self = [super init];
    if (self) {  
        [self loadFakeMessages];
        self.users = @{ @"1" : @"Alex",
                        @"2" : @"Sam",
                        @"3" : @"Kev",
                        @"4" : @"Ray" };
        JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init];       
        self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]];
        self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleGreenColor]];
    }
    return self;
}

Create the Fake Messages

- (void)loadFakeMessages { ... }

We can use this back in our MessageViewController.m

#pragma mark - View lifecycle
  • viewDidLoad
  • viewWillAppear
  • viewDidAppear

Since I’m not using Avatars add this to viewDidLoad

self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero; self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;

Also return nil in ‘avatarImageDataForItemAtIndexPath’

- (id<JSQMessageAvatarImageDataSource>)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageDataForItemAtIndexPath:(NSIndexPath *)indexPath {
    ...
    return nil;
}

Implement these other methods.

#pragma mark - Custom menu actions for cells
#pragma mark - Actions
#pragma mark - JSQMessagesViewController method overrides
#pragma mark - JSQMessages CollectionView DataSource

Choose a user here

  • senderId
  • senderId
#pragma mark - UICollectionView DataSource
#pragma mark - UICollectionView Delegate
#pragma mark - Custom menu items
#pragma mark - JSQMessages collection view flow layout delegate
#pragma mark - Adjusting cell label heights
#pragma mark - Responding to collection view tap events
#pragma mark - JSQMessagesComposerTextViewPasteDelegate methods
#pragma mark - JSQMessagesViewAccessoryDelegate methods

Changing some settings

I didn’t want the Add Attachments button.

viewDidLoad

self.inputToolbar.contentView.leftBarButtonItem = nil;

Now just to swap out the messages to my XMPP Server…

XMPPFramework

XMPPFramework

Create an Xcode project.

Open terminal at that folder location.

pod init
pod 'XMPPFramework', :git => "https://github.com/robbiehanson/XMPPFramework.git", :branch => 'master'
@import XMPPFramework;

Getting Started

Tutorials

Had issues setting up Ejabbered on the mac.

I’m using an OpenFire server I setup so used that instead, with a couple of clients connected to there instead.

Clients

Stack Overflow


Setting the Server

set the server url

set the port


Delivery Receipts

Read Receipts


Group Chat

MapKit

So I’ve been learning MapKit this week.

It’s been a fun new framework to look into.

Create an Annotation so you can have custom properties.

@interface Annotation : NSObject<MKAnnotation>

Add a category to it so you have a way of telling what it is.

This could be an enum.

In the Delegate method

- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation

You can use the category to set up some properties like a button and the pin colour.

I’m wondering if the “reuseIdentifier” should be unique here too?

You’ve added a button, now how to call it?

You could add a target and action

[rightButton addTarget:## action:## forControlEvents:UIControlEventTouchUpInside];

but instead you can use the “calloutAccessoryControlTapped” delegate method.

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control

As this isn’t a  “MKAnnotation” like “viewForAnnotation” but an “MKAnnotationView” we need to get the annotation from the passed view.

if ([view.annotation isKindOfClass:[OotAnnotation class]]) { ... }

Then call pfs,

Then in the “prepareForSegue” you need to get the Annotation when you click on a Pin button, this can be cast from the Sender

if ([view.annotation isKindOfClass:[OotAnnotation class]]) { ... }
Annotation *annotation = (Annotation *)[(MKAnnotationView *)sender annotation];

You could then pass your custom Location details via the ViewController. (See code)

Searching for locations to add to the map:

MKLocalSearchRequest

Adding a route, need to investigate this more.

MKPolyline

All Code

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "Location.h"
@interface OotAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
NSInteger categoryID;
}
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property NSInteger categoryID;
@property (nonatomic, strong) Location *locationDetails;
typedef NS_ENUM(NSInteger, Category) {
PinCategoryPlace,
PinCategoryUser,
PinCategoryPub,
PinCategoryNightclub,
PinCategoryFood
};
@end
view raw Annotation.h hosted with ❤ by GitHub
#import "Annotation.h"
@implementation OotAnnotation
@end
view raw Annotation.m hosted with ❤ by GitHub
#import <Foundation/Foundation.h>
@interface Location : NSObject
@property (nonatomic, strong) NSString *locationName;
@property double locationRating;
@property NSInteger locationNumOfReviews;
@property (nonatomic, strong) NSString *locationDetails;
@property (nonatomic, strong) NSString *locationImageUrl;
@property (nonatomic, strong) NSString *locationEmail;
@property (nonatomic, strong) NSString *locationPhone;
@property (nonatomic, strong) NSString *locationRouteDetails;
@property (nonatomic, strong) NSString *locationNotes;
@property (nonatomic, strong) NSString *locationDeal;
- (id)initWithName:(NSString *)name rating:(double)rating numOfReviews:(NSInteger)numOfReviews details:(NSString *)details imageUrl:(NSString *)imageUrl email:(NSString *)email phone:(NSString *)phone routeDetails:(NSString *)routeDetails notes:(NSString *)notes deal:(NSString *)deal;
+ (id)locationWithName:(NSString *)name rating:(double)rating numOfReviews:(NSInteger)numOfReviews details:(NSString *)details imageUrl:(NSString *)imageUrl email:(NSString *)email phone:(NSString *)phone routeDetails:(NSString *)routeDetails notes:(NSString *)notes deal:(NSString *)deal;
@end
view raw Location.h hosted with ❤ by GitHub
#import "Location.h"
@implementation Location
- (id)initWithName:(NSString *)name rating:(double)rating numOfReviews:(NSInteger)numOfReviews details:(NSString *)details imageUrl:(NSString *)imageUrl email:(NSString *)email phone:(NSString *)phone routeDetails:(NSString *)routeDetails notes:(NSString *)notes deal:(NSString *)deal {
self = [super init];
if (self) {
self.locationName = name;
self.locationRating = rating;
self.locationNumOfReviews = numOfReviews;
self.locationDetails = details;
self.locationImageUrl = imageUrl;
self.locationEmail = email;
self.locationPhone = phone;
self.locationRouteDetails = routeDetails;
self.locationNotes = notes;
self.locationDeal = deal;
}
return self;
}
+ (id)locationWithName:(NSString *)name rating:(double)rating numOfReviews:(NSInteger)numOfReviews details:(NSString *)details imageUrl:(NSString *)imageUrl email:(NSString *)email phone:(NSString *)phone routeDetails:(NSString *)routeDetails notes:(NSString *)notes deal:(NSString *)deal {
return [[self alloc] initWithName:name rating:rating numOfReviews:numOfReviews details:details imageUrl:imageUrl email:email phone:phone routeDetails:routeDetails notes:notes deal:deal];
}
@end
view raw Location.m hosted with ❤ by GitHub
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import <MapKit/MKAnnotation.h>
#import <AddressBook/AddressBook.h>
#import <Contacts/Contacts.h>
@interface MapViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>
@property (nonatomic, strong) IBOutlet MKMapView *mapView;
@property (nonatomic, retain) CLLocationManager *locationManager;
@property (nonatomic, retain) MKPolyline *routeLine;
@property (nonatomic, retain) MKPolylineView *routeLineView;
@end
#import "MapViewController.h"
#import "Annotation.h"
#import "LocationViewController.h"
@interface MapViewController ()
@end
@implementation MapViewController
#pragma mark - View Methods
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.mapView.delegate = self;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
#ifdef __IPHONE_8_0
if(IS_OS_8_OR_LATER) {
// Use one or the other, not both. Depending on what you put in info.plist
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
}
#endif
[self.locationManager startUpdatingLocation];
_mapView.showsUserLocation = YES;
[_mapView setMapType:MKMapTypeStandard];
[_mapView setZoomEnabled:YES];
[_mapView setScrollEnabled:YES];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:YES];
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
NSLog(@"%@", [self deviceLocation]);
//View Area
MKCoordinateRegion region = { { 0.0, 0.0 }, { 0.0, 0.0 } };
region.center.latitude = self.locationManager.location.coordinate.latitude;
region.center.longitude = self.locationManager.location.coordinate.longitude;
region.span.longitudeDelta = 0.005f;
region.span.longitudeDelta = 0.005f;
[_mapView setRegion:region animated:YES];
//[self addLocation:@"Protirus" subTitle:@"Office" lat:54.969617 lon:-1.618393];
//[self addLocation:@"Town Wall" subTitle:@"Gastropub" lat:54.9694 lon:-1.6184]; //54.9694° N, 1.6184° W
Location *locationDetails = [Location locationWithName:@"Town Wall" rating:4.2 numOfReviews:73 details:@"Gastropub" imageUrl:@"TheTownWall" email:@"hayley@thetownwall.com" phone:@"01912323000" routeDetails:@"Town Wall - 5 minutes away - marked" notes:@"Plan to stay for an hour perhaps" deal:@"DEAL - 2 for 1 Drinks"];
[self addPinWithTitle:@"Protirus" subTitle:@"Office" lat:54.969617 lon:-1.618393 category:PinCategoryPlace locationDetails:nil];
[self addPinWithTitle:@"Town Wall" subTitle:@"Gastropub" lat:54.9694 lon:-1.6184 category:PinCategoryPub locationDetails:locationDetails];
CLLocationCoordinate2D coordinateArray[2];
coordinateArray[0] = CLLocationCoordinate2DMake(54.969617, -1.618393);
coordinateArray[1] = CLLocationCoordinate2DMake(54.9694, -1.6184);
[self addRoute:coordinateArray];
[self addFood];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Locations
// http://stackoverflow.com/a/27164326
- (void)addPinWithTitle:(NSString *)title subTitle:(NSString *)subTitle lat:(double)lat lon:(double)lon category:(int)category locationDetails:(OotLocation *)locationDetails {
CLLocationCoordinate2D coordinates = CLLocationCoordinate2DMake(lat, lon);
Annotation *annotation = [[Annotation alloc] init];
annotation.coordinate = coordinates;
annotation.title = title;
annotation.subtitle = subTitle;
annotation.categoryID = category;
annotation.locationDetails = locationDetails;
[self.mapView addAnnotation:annotation];
}
// http://stackoverflow.com/a/14951825
- (void)addFood {
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = @"Restaurants";
request.region = self.mapView.region;
MKLocalSearch *localSearch = [[MKLocalSearch alloc] initWithRequest:request];
[localSearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
NSMutableArray *annotations = [NSMutableArray array];
[response.mapItems enumerateObjectsUsingBlock:^(MKMapItem *item, NSUInteger idx, BOOL *stop) {
Annotation *annotation = [[Annotation alloc] init];
annotation.coordinate = item.placemark.location.coordinate;
annotation.title = item.name;
annotation.subtitle = item.placemark.addressDictionary[@"Street"]; //item.placemark.thoroughfare;
//item.placemark.addressDictionary[CNMutablePostalAddress.street]; // kABPersonAddressStreetKey]; //item.phoneNumber;
annotation.categoryID = PinCategoryFood;
annotation.locationDetails = nil;
[annotations addObject:annotation];
}];
[self.mapView addAnnotations:annotations];
}];
}
// http://stackoverflow.com/a/10598646
- (void)addRoute:(CLLocationCoordinate2D [])coordinateArray {
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:2];
[self.mapView setVisibleMapRect:[self.routeLine boundingMapRect]]; //If you want the route to be visible
[self.mapView addOverlay:self.routeLine];
}
#pragma mark - Helper Methods
- (NSString *)deviceLocation {
return [NSString stringWithFormat:@"latitude: %f longitude: %f", self.locationManager.location.coordinate.latitude, self.locationManager.location.coordinate.longitude];
}
#pragma mark - Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"showLocation"]) {
//LocationViewController *lvc = (LocationViewController *)[segue destinationViewController];
UINavigationController *navController = [segue destinationViewController];
LocationViewController *lvc = (LocationViewController *)([navController viewControllers][0]);
//http://stackoverflow.com/a/25483130
Annotation *annotation = (Annotation *)[(MKAnnotationView *)sender annotation];
lvc.location = annotation.locationDetails;
}
}
#pragma mark - MapView Delegate
//https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/AnnotatingMaps/AnnotatingMaps.html#//apple_ref/doc/uid/TP40009497-CH6-SW1
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// Try to dequeue an existing pin view first (code not shown).
// If no pin view already exists, create a new one.
MKPinAnnotationView *customPinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"anything"];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Because this is an iOS app, add the detail disclosure button to display details about the annotation in another view.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
//[rightButton addTarget:nil action:nil forControlEvents:UIControlEventTouchUpInside];
//customPinView.rightCalloutAccessoryView = rightButton;
NSString* imageName = @"Pin";
if ([annotation isKindOfClass:[Annotation class]]) {
long categoryId = [(Annotation *)annotation categoryID];
switch (categoryId) {
case PinCategoryUser:
imageName = @"User";
//customPinView.pinTintColor = MKPinAnnotationColorRed;
break;
case PinCategoryPub:
imageName = @"Beer";
customPinView.pinTintColor = [UIColor orangeColor];
customPinView.rightCalloutAccessoryView = rightButton;
break;
case PinCategoryNightclub:
imageName = @"Nightclub";
//customPinView.pinTintColor = MKPinAnnotationColorPurple;
break;
case PinCategoryFood:
imageName = @"Food";
//customPinView.pinTintColor = MKPinAnnotationColorPurple;
customPinView.pinTintColor = [UIColor redColor];
break;
default:
imageName = @"Pin";
//customPinView.pinTintColor = MKPinAnnotationColorRed;
break;
}
//if([(OotAnnotation *)annotation categoryID] == PinCategoryUser) {
// Add a custom image to the left side of the callout.
//}
}
// Add a custom image to the left side of the callout.
UIImageView *myCustomImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
customPinView.leftCalloutAccessoryView = myCustomImage;
return customPinView;
}
// http://stackoverflow.com/a/6941743
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
if ([view.annotation isKindOfClass:[Annotation class]]) {
[self performSegueWithIdentifier:@"showLocation" sender:view];
}
}
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
if(overlay == self.routeLine) {
if(nil == self.routeLineView) {
self.routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor yellowColor];
self.routeLineView.strokeColor = [UIColor yellowColor];
self.routeLineView.lineWidth = 10;
}
return self.routeLineView;
}
return nil;
}
@end

Goals for 2018

Xamarin

I’d started some training last year but didn’t make any useful apps, I’d like to progress this to a production one in 2018.

.NET

I’d like to make progress in either or both of my Tracking app ideas.

599CD

Actually release it, I’ve been holding off hoping the API will be re-written, maybe I should just release, then update when it is.

Although releasing the Android at the same time would be nice.

Certs

MS: Programming in HTML5 with JavaScript and CSS3

MCSA: Web Applications

MCPS: Developing ASP.NET MVC Web Applications

Microsoft Certified Solution Developer: App Builder

Goals for 2017 – Review

599CD

  • iOS
  • Re-wrote API interaction using the Facade pattern
  • Android
  • Asked a friend to help write this

Still waiting on Rich to make a proper API.

Jam Jar

Worked with PassSlot and have a local iOS app that creates them, with images

Jam Jar Cinema Wallet Passes

.NET Apps

I worked a little on a GNVQ tracking app for Learning Objectives, the back end is mostly there, still working on the UI.

In general I was working on a major iOS project at work so didn’t have a massive amount of spare time, learned lots of new APIs and techniques in ObjC and iOS dev which was great.

Previous

UIBarButtonItem Image and Text

I want to add an image and text to a BarButtonItem but can’t in IB.

 

I looked into EdgeInsets but couldn’t get them to work with the image.

 

I’d also like to shrink the image

// https://stackoverflow.com/a/18853240/2895831
UIView *rightButtonView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 110, 50)];

UIImage *buttonImage = [UIImage imageNamed:@"LocationArrow"];
//UIEdgeInsets edgeInsets = UIEdgeInsetsMake(50, 50, 50, 50);
//UIImage *buttonImage = [[UIImage imageNamed:@"LocationArrow"] resizableImageWithCapInsets:edgeInsets];
//UIImage(CGImage: originalImage!.CGImage!, scale: 5, orientation: originalImage!.imageOrientation)
// https://stackoverflow.com/a/38523085/2895831
buttonImage = [UIImage imageWithCGImage:buttonImage.CGImage
 scale:4//CGImageGetHeight(buttonImage.CGImage)/2
 orientation:UIImageOrientationUp];

UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeSystem];
rightButton.backgroundColor = [UIColor clearColor];
rightButton.frame = rightButtonView.frame;
[rightButton setImage:buttonImage forState:UIControlStateNormal];
[rightButton setTitle:NSLocalizedString(@"My Location", nil) forState:UIControlStateNormal];
rightButton.tintColor = [UIColor whiteColor];
rightButton.autoresizesSubviews = YES;
rightButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
rightButton.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
[rightButton addTarget:self action:@selector(myLocationAction:) forControlEvents:UIControlEventTouchUpInside];

CGFloat padding = 10.0f;
[rightButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 0, 0, padding)];
//[rightButton setContentEdgeInsets:UIEdgeInsetsMake(0, 0, 0, padding)];
//[rightButton setContentEdgeInsets:UIEdgeInsetsMake(padding, padding, padding, padding)];
//[rightButton setImageEdgeInsets:UIEdgeInsetsMake(padding, 0, padding, 0)]; //0, padding, 0, padding
//[rightButton setImageEdgeInsets:UIEdgeInsetsMake(padding, padding, padding, padding)];

[rightButtonView addSubview:rightButton];

UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc] initWithCustomView:rightButtonView];
self.navigationItem.rightBarButtonItem = rightBarButton;

 

Image

[UIImage imageWithCGImage:buttonImage.CGImage scale:4 orientation:UIImageOrientationUp];

Pick your method to scale

CGImageGetHeight(buttonImage.CGImage)/2

Or

CGImageGetWidth(...)/DESIREDWIDTH

 

IB

There is the handy

Bar Item

Image Inset

 

UIButton VerticalLayout

https://stackoverflow.com/a/22621613/2895831

//https://stackoverflow.com/a/22621613/2895831
@interface UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;
@end
@implementation UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding
{
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;
CGFloat totalHeight = (imageSize.height + titleSize.height + padding);
self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
0.0f,
0.0f,
- titleSize.width);
self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
- imageSize.width,
- (totalHeight - titleSize.height),
0.0f);
// self.contentEdgeInsets = UIEdgeInsetsMake(0.0f,
// 0.0f,
// titleSize.height,
// 0.0f);
CGFloat inset = (self.frame.size.height - totalHeight)/2;
self.contentEdgeInsets = UIEdgeInsetsMake(inset, 0.0f, inset, 0.0f);
}
- (void)centerVertically
{
const CGFloat kDefaultPadding = 6.0f;
[self centerVerticallyWithPadding:kDefaultPadding];
}
@end

I added some buttons into a UIStackView but the image overlaps the button.

I modified the contentEdgeInsets

CGFloat inset = (self.frame.size.height - totalHeight)/2;
self.contentEdgeInsets = UIEdgeInsetsMake(inset, 0.0f, inset, 0.0f);

 

 

Pollen Today Widget

I want a today Widget to show pollen count.

I downloaded:

Clarityn’s Pollen Forecast UK by Bayer HealthCare AG

https://appsto.re/gb/tPFfw.i

They have a website

http://www.claritynallergy.co.uk/allergyforecast/?zip=X

It doesn’t have the Widget I wanted so I started looking for an API.


POST http://www.claritynallergy.co.uk/webservice/allergyforecast.php

Header

Accept: application/json

Body

Form URL-Encoded

zip=NE11EE

Response

"{\"Durham & Newcastle\":{\"1499515201\":{\"precipitation\":8,\"maxTemp\":21,\"minTemp\":16,\"pollenLevel\":\"H\",\"humidity\":83,\"windSpeed\":7,\"weatherCode\":7,\"uvIndex\":5,\"pollenType\":\"Grass\"},\"1499601600\":{\"precipitation\":18,\"maxTemp\":18,\"minTemp\":15,\"pollenLevel\":\"H\",\"humidity\":80,\"windSpeed\":18,\"weatherCode\":7,\"uvIndex\":4,\"pollenType\":\"Grass\"},\"1499688000\":{\"precipitation\":23,\"maxTemp\":18,\"minTemp\":14,\"pollenLevel\":\"M\",\"humidity\":69,\"windSpeed\":11,\"weatherCode\":3,\"uvIndex\":7,\"pollenType\":\"Grass\"}}}"

Images

http://www.claritynallergy.co.uk/static/media/images/claritynallergy/7-trans.png&#8221; : 


I found Benadryl has one.

https://www.benadryl.co.uk/social-pollen-count


GET https://socialpollencount.co.uk/api/forecast?location=%5BLAT,LON]

Response

{"status_code":"200 OK","date":"2017-07-02T07:13:47+00:00","forecast":[{"date":"2017-06-29T08:00:01+00:00","pollen_count":"Low"},{"date":"2017-06-30T08:00:01+00:00","pollen_count":"Low"},{"date":"2017-07-01T08:00:01+00:00","pollen_count":"High"},{"date":"2017-07-02T08:00:01+00:00","pollen_count":"High","temperature":"18","weather_code":"01","weather":"Sunny"},{"date":"2017-07-03T08:00:01+00:00","pollen_count":"High","temperature":"18","weather_code":"07","weather":"Medium-level cloud"},{"date":"2017-07-04T08:00:01+00:00","pollen_count":"Moderate","temperature":"14","weather_code":"12","weather":"Light rain"}]}

Images

https://socialpollencount.co.uk/images/content/weather/07.pnp&#8221; :


App: https://itunes.apple.com/gb/app/benadryl-social-pollen-count/id638068252?mt=8

App doesn’t exist.


Progress
I’ve started building my own

img_4314 img_4315-1 img_4317-1
The Widget needs a lot of work but the basis is there, I’ve built a framework that gets all the data. Now to finish the UI…

Carousel View

iCarousel

I wanted to add an animation when dismissing, a button at the top and bottom of the view, one to dismiss, one to save, and move off in that direction.


– (void)removeItemAtIndex:(NSInteger)index animated:(BOOL)animated {
//Amended method below to slide Up/Down
}
– (void)saveItemAtIndex:(NSInteger)index animated:(BOOL)animated {
index = [self clampedIndex:index];
UIView *itemView = [self itemViewAtIndex:index];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:itemView.superview];
[UIView setAnimationDidStopSelector:@selector(removeFromSuperview)];
[self performSelector:@selector(queueItemView:) withObject:itemView afterDelay:0.1];
itemView.superview.layer.opacity = 0.0;
itemView.frame = CGRectOffset(itemView.frame, 0, 250); //Swap to -250 to change direction
[UIView commitAnimations];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelay:0.1];
[UIView setAnimationDuration:INSERT_DURATION];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(depthSortViews)];
[self removeViewAtIndex:index];
_numberOfItems –;
_wrapEnabled = !![self valueForOption:iCarouselOptionWrap withDefault:_wrapEnabled];
[self updateNumberOfVisibleItems];
_scrollOffset = self.currentItemIndex;
[self didScroll];
[UIView commitAnimations];
}

view raw

iCarousel.m

hosted with ❤ by GitHub

MDCSwipeToChoose

TinderSimpleSwipeCards

@clsource added other directions

Directions


#import <UIKit/UIKit.h>
@protocol CardDelegate;
typedef NS_ENUM(NSUInteger, kCardState) {
kCardStateIdle = 0,
kCardStateMoving = 1,
kCardStateGone = 2
};
// The Position of the Card
typedef NS_ENUM(NSInteger, kCardPosition) {
kCardPositionTop = 0,
kCardPositionBack = 1
};
/*!
* Implements the Views inside the Cards
*/
@interface CardView : UIView
#pragma mark – Properties
/*!
* Defines the current state of the card
*/
@property (nonatomic) kCardState state;
/*!
* The Weight of the Card indicates its position in the stack
*/
@property (nonatomic) kCardPosition position;
/*!
* The delegate that will listen to the notifications
* on created on pan gesture recognizers
*/
@property (nonatomic, weak) id <CardDelegate> delegate;
/*!
* Where in the array is located the view
*/
@property (nonatomic) NSInteger index;
#pragma mark – Methods
#pragma mark Init
/*!
* Designated Initializer
*
* This method inits the view
* and set the to fetch
* also renders the view with 's data
*
* @param CGRect frame for view's bounds
* @param ViewDelegate delegate to send events
* @param * to render the view info
*/
– (void) setFrame:(CGRect)frame delegate:(id<CardDelegate>) delegate;
#pragma mark Instance
/*!
* Swipes the view to the left
* programatically
*/
– (void) swipeLeft;
/*!
* Swipes the view to the right
* programatically
*/
– (void) swipeRight;
/*!
* Swipes the view up
* programatically
*/
– (void) swipeUp;
/*!
* Swipes the view down
* programatically
*/
– (void) swipeDown;
/*!
* Cancels a Swipe
*/
– (void) cancelSwipe;
@end
@protocol CardDelegate <NSObject>
#pragma mark – Delegate Methods
/*!
* Method called when the view will begin pan gesture
* @param Card * Card
*/
– (void) willBeginSwipeInCard : (CardView *) card;
/*!
* Method called when the view did end pan gesture
* @param Card * Card
*/
– (void) didEndSwipeInCard : (CardView *) card;
/*!
* Method called when the view did not reach a detected position
* @param Card * Card
*/
– (void) didCancelSwipeInCard : (CardView *) card;
/*!
* Method called when the view was swiped left
* @param Card * Card
*/
– (void) swipedLeftInCard : (CardView *) card;
/*!
* Method called when the view was swiped right
* @param Card * Card
*/
– (void) swipedRightInCard : (CardView *) card;
/*!
* Method called when the view was swiped up
* @param Card * Card
*/
– (void) swipedUpInCard : (CardView *) card;
/*!
* Method called when the view was swiped down
* @param Card * Card
*/
– (void) swipedDownInCard : (CardView *) card;
/*!
* Method called when the view button was pressed
* @param Card * Card;
*/
– (void) wasTouchedDownInCard : (CardView *) card;
/*!
* Method called when the state was changed
*
* @param Card * Card;
*/
– (void) didChangeStateInCard: (CardView *) card;
/*!
* Ask the delegate if the card should move
*
* @param Card the card
*
* @return YES if the card should move
*/
– (BOOL) shouldMoveCard: (CardView *) card;
@end

view raw

CardView.h

hosted with ❤ by GitHub


#import "CardView.h"
// Constants Declaration
// This constant represent the distance from the center
// where the action applies. A higher value means that
// the user needs to draw the view further in order for
// the action to be executed.
#define ACTION_MARGIN 120
// This constant is the distance from the center. But vertically
#define Y_ACTION_MARGIN 100
// This constant represent how quickly the view shrinks.
// The view will shrink when is beign moved out the visible
// area.
// A Higher value means slower shrinking
#define SCALE_QUICKNESS 4
// This constant represent how much the view shrinks.
// A Higher value means less shrinking
#define SCALE_MAX .93
// This constant represent the rotation angle
#define ROTATION_ANGLE M_PI / 8
// This constant represent the maximum rotation angle
// allowed in radiands.
// A higher value enables more rotation for the view
#define ROTATION_MAX 1
// This constant defines how fast
// the rotation should be.
// A higher values means a faster rotation.
#define ROTATION_QUICKNESS 320
@interface CardView()
// Internal Variables
@property (nonatomic) CGFloat xFromCenter;
@property (nonatomic) CGFloat yFromCenter;
@property (nonatomic) CGPoint originalPoint;
@end
@implementation CardView
@synthesize state = _state;
– (kCardState) state {
if (!_state) {
_state = kCardStateIdle;
}
return _state;
}
– (void) setState:(kCardState) state {
if (_state != state) {
_state = state;
[self.delegate didChangeStateInCard:self];
}
}
– (kCardPosition) position {
if (!_position) {
_position = kCardPositionTop;
}
return _position;
}
#pragma mark – Init
/*!
* Designated Initializer
*
* This method inits the view
* and set the feed to fetch
* also renders the view with feed's data
*
* @param CGRect frame for view's bounds
* @param CardDelegate delegate to send events
*/
– (void) setFrame:(CGRect)frame delegate:(id<CardDelegate>) delegate {
self.frame = frame;
self.delegate = delegate;
[self setupView];
[self registerSwipeGestures];
}
#pragma mark Init Helper Methods
/*!
* Round corners of the view
* and draw a shadow
* Do another view related
* actions required on init
*/
– (void) setupView {
// Draw Shadow
// And round the view
self.layer.cornerRadius = 10;
self.layer.shadowRadius = 3;
self.layer.shadowOpacity = 0.2;
self.layer.shadowOffset = CGSizeMake(1,1);
}
/*!
* Register Pan Gesture
* and delegates
*/
– (void) registerSwipeGestures {
UIPanGestureRecognizer * panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self addGestureRecognizer:panRecognizer];
}
#pragma mark Pan Gesture Recognizer Handlers
/*!
* This is the main method for motion detection
* its called several times a second when the
* fingers are moved across the screen.
*/
– (void) handlePanGesture: (UIPanGestureRecognizer *) panRecognizer {
// This extracts the coordinate data from the swipe movement.
// how much did fingers move.
// We need to know the X position.
// A positive value means movement to the right.
// A negative value means movement to the left.
self.xFromCenter = [panRecognizer translationInView:self].x;
// We need to know the Y position.
// A positive value means up movement.
// A negative value means down movement.
self.yFromCenter = [panRecognizer translationInView:self].y;
// Now we check on wich state our swipe is
// if is its starting, in the middle or ended
// the swiping.
switch (panRecognizer.state) {
// Swiping just started
case UIGestureRecognizerStateBegan: {
// We will save the original point
// when we started
self.originalPoint = self.center;
if (self.delegate) {
if ([self.delegate shouldMoveCard:self]) {
// And tell the delegate
// that the movement just started
[self.delegate willBeginSwipeInCard:self];
}
}
break;
}
// Swiping is in course
case UIGestureRecognizerStateChanged: {
// Animate the view
if (self.delegate) {
// If delegate is present
// ask if it should move the card
if ([self.delegate shouldMoveCard:self]) {
[self animateView];
}
} else {
[self animateView];
}
break;
}
// Swiping ended
case UIGestureRecognizerStateEnded: {
if (self.delegate) {
if ([self.delegate shouldMoveCard:self]) {
[self detectSwipeDirection];
}
} else {
[self detectSwipeDirection];
}
break;
}
default:
break;
}
}
#pragma mark Helper Methods
/*!
* Rotates the view
* and changes its scale and position
*/
– (void) animateView {
// Do some black magic math
// for rotating and scale
// Gets the rotation quickness
// see constants.
CGFloat rotationQuickness = MIN(self.xFromCenter / ROTATION_QUICKNESS, ROTATION_MAX);
// Change the rotation in radians
CGFloat rotationAngle = (CGFloat) (ROTATION_ANGLE * rotationQuickness);
// the height will change when the view reaches certain point
CGFloat scale = MAX(1 – fabsf(rotationQuickness) / SCALE_QUICKNESS, SCALE_MAX);
// move the object center depending on the coordinate
self.center = CGPointMake(self.originalPoint.x + self.xFromCenter,
self.originalPoint.y + self.yFromCenter);
// rotate by the angle
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(rotationAngle);
// scale depending on the rotation
CGAffineTransform scaleTransform = CGAffineTransformScale(rotateTransform, scale, scale);
// apply transformations
self.transform = scaleTransform;
// Change state
[self changeStateToMoving];
}
/*!
* With all the values fetched
* from the pan gesture
* gets the direction of the swipe
* when the swipe is done
*/
– (void) detectSwipeDirection {
if (self.xFromCenter > ACTION_MARGIN) {
[self performRightAnimation];
} else if (self.xFromCenter < – ACTION_MARGIN) {
[self performLeftAnimation];
} else if(self.yFromCenter < – Y_ACTION_MARGIN) {
[self performUpAnimation];
} else if(self.yFromCenter > Y_ACTION_MARGIN) {
[self performDownAnimation];
} else {
[self performCenterAnimation];
}
// And tell the delegate
// that the swipe just finished
[self.delegate didEndSwipeInCard:self];
}
– (void) changeStateToIdle {
// Idle state indicates that the card
// is showing in the view, but not moving.
self.state = kJMPTFeedCardStateIdle;
}
– (void) changeStateToGone {
// Gone state indicates that the card
// was removed from the view
self.state = kJMPTFeedCardStateGone;
}
– (void) changeStateToMoving {
self.state = kJMPTFeedCardStateMoving;
// Cancel Swipe if Moving but not should
if (self.delegate && ![self.delegate shouldMoveCard:self]) {
[self performCenterAnimation];
}
}
#pragma mark Animation Methods
/*!
* The view will go to the right
*/
– (void) performRightAnimation {
CGPoint finishPoint = CGPointMake(500, 2 * self.yFromCenter + self.originalPoint.y);
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.center = finishPoint;
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self changeStateToGone];
[self.delegate swipedRightInCard:self];
}];
}
/*!
* The view will got to the left
*/
– (void) performLeftAnimation {
CGPoint finishPoint = CGPointMake(-500, 2 * self.yFromCenter + self.originalPoint.y);
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.center = finishPoint;
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self changeStateToGone];
[self.delegate swipedLeftInCard:self];
}];
}
/*!
* The view will go up
* do not remove from view
* just perfom some goofy moves
*/
– (void) performUpAnimation {
[UIView animateWithDuration:0.7
delay:0
usingSpringWithDamping:0.56
initialSpringVelocity:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.center = self.originalPoint;
self.transform = CGAffineTransformMakeRotation(0);
} completion:^(BOOL finished) {
[self changeStateToIdle];
[self.delegate swipedUpInCard:self];
}];
}
/*!
* The view will go down
* do not remove from view
* just perfom some goofy moves
*/
– (void) performDownAnimation {
[UIView animateWithDuration:0.7
delay:0
usingSpringWithDamping:0.56
initialSpringVelocity:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.center = self.originalPoint;
self.transform = CGAffineTransformMakeRotation(0);
} completion:^(BOOL finished) {
[self changeStateToIdle];
[self.delegate swipedDownInCard:self];
}];
}
/*!
* The view will go to the center
* (cancel swipe) and reset the values
*/
– (void) performCenterAnimation {
[UIView animateWithDuration:0.7
delay:0
usingSpringWithDamping:0.56
initialSpringVelocity:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.center = self.originalPoint;
self.transform = CGAffineTransformMakeRotation(0);
} completion:^(BOOL finished) {
[self changeStateToIdle];
[self.delegate didCancelSwipeInCard:self];
}];
}
#pragma mark – Programatically Swipe Methods
– (void) swipeLeft {
// The same animation but with a delay
CGPoint finishPoint = CGPointMake(-500, 2 * self.yFromCenter + self.originalPoint.y);
[self changeStateToMoving];
[UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.center = finishPoint;
} completion:^(BOOL finished) {
if (finished) {
[self removeFromSuperview];
[self changeStateToGone];
[self.delegate swipedLeftInCard:self];
}
}];
}
– (void) swipeRight {
// The same animation, but with a delay
CGPoint finishPoint = CGPointMake(500, 2 * self.yFromCenter + self.originalPoint.y);
[self changeStateToMoving];
[UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.center = finishPoint;
} completion:^(BOOL finished) {
if (finished) {
[self removeFromSuperview];
[self changeStateToGone];
[self.delegate swipedRightInCard:self];
}
}];
}
– (void) swipeUp {
//TODO: Implement this
}
– (void) swipeDown {
//TODO: Implement this
}
– (void) cancelSwipe {
[self performCenterAnimation];
}
#pragma mark – IBActions
– (IBAction) cardButton: (id)sender {
[self.delegate wasTouchedDownInCard:self];
}
@end

view raw

CardView.m

hosted with ❤ by GitHub

Design a site like this with WordPress.com
Get started