Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,40 @@
//
// OrderedDictionary.h
// OrderedDictionary
//
// Modified version (Added indexForKey/Value functions)
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software. Permission is granted to anyone to
// use this software for any purpose, including commercial applications, and to
// alter it and redistribute it freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source
// distribution.
//
#import <Foundation/Foundation.h>
@interface OrderedDictionary : NSMutableDictionary
{
NSMutableDictionary *dictionary;
NSMutableArray *array;
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex;
- (id)keyAtIndex:(NSUInteger)anIndex;
- (NSUInteger)indexForValue:(id)value;
- (NSUInteger)indexForKey:(id)key;
- (NSEnumerator *)reverseKeyEnumerator;
@end

View File

@@ -0,0 +1,167 @@
//
// OrderedDictionary.m
// OrderedDictionary
//
// Modified version (Added indexForKey/Value functions)
//
// Created by Matt Gallagher on 19/12/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software. Permission is granted to anyone to
// use this software for any purpose, including commercial applications, and to
// alter it and redistribute it freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source
// distribution.
//
#import "OrderedDictionary.h"
NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent)
{
NSString *objectString;
if ([object isKindOfClass:[NSString class]])
{
objectString = (NSString *)[[object retain] autorelease];
}
else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent];
}
else if ([object respondsToSelector:@selector(descriptionWithLocale:)])
{
objectString = [(NSSet *)object descriptionWithLocale:locale];
}
else
{
objectString = [object description];
}
return objectString;
}
@implementation OrderedDictionary
- (id)init
{
return [self initWithCapacity:0];
}
- (id)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity];
array = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
- (void)dealloc
{
[dictionary release];
[array release];
[super dealloc];
}
- (id)copy
{
return [self mutableCopy];
}
- (void)setObject:(id)anObject forKey:(id)aKey
{
if (![dictionary objectForKey:aKey])
{
[array addObject:aKey];
}
[dictionary setObject:anObject forKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[dictionary removeObjectForKey:aKey];
[array removeObject:aKey];
}
- (NSUInteger)count
{
return [dictionary count];
}
- (id)objectForKey:(id)aKey
{
return [dictionary objectForKey:aKey];
}
- (NSEnumerator *)keyEnumerator
{
return [array objectEnumerator];
}
- (NSEnumerator *)reverseKeyEnumerator
{
return [array reverseObjectEnumerator];
}
- (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex
{
if ([dictionary objectForKey:aKey])
{
[self removeObjectForKey:aKey];
}
[array insertObject:aKey atIndex:anIndex];
[dictionary setObject:anObject forKey:aKey];
}
- (id)keyAtIndex:(NSUInteger)anIndex
{
return [array objectAtIndex:anIndex];
}
- (NSUInteger)indexForKey:(id)key
{
return [array indexOfObject:key];
}
- (NSUInteger)indexForValue:(id)value
{
NSArray *keys = [self allKeysForObject:value];
if ([keys count] > 0)
{
return [self indexForKey:[keys objectAtIndex:0]];
}
return NSNotFound;
}
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *indentString = [NSMutableString string];
NSUInteger count = level;
for (NSUInteger i = 0; i < count; i++)
{
[indentString appendFormat:@" "];
}
NSMutableString *description = [NSMutableString string];
[description appendFormat:@"%@{\n", indentString];
for (NSObject *key in self)
{
[description appendFormat:@"%@ %@ = %@;\n", indentString,
DescriptionForObject(key, locale, level),
DescriptionForObject([self objectForKey:key], locale, level)];
}
[description appendFormat:@"%@}\n", indentString];
return description;
}
@end

View File

@@ -0,0 +1,34 @@
/*
Additions to Cocoa touch classes
Copyright 2013 Thincast Technologies GmbH, Authors: Dorian Johnson, Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
#import <Foundation/Foundation.h>
@interface NSObject (TSXAdditions)
- (void)setValuesForKeyPathsWithDictionary:(NSDictionary *)keyedValues;
@end
#pragma mark -
@interface NSString (TSXAdditions)
+ (NSString *)stringWithUUID;
- (NSData *)dataFromHexString;
+ (NSString *)hexStringFromData:(const unsigned char *)data
ofSize:(unsigned int)size
withSeparator:(NSString *)sep
afterNthChar:(int)sepnth;
@end
@interface NSDictionary (TSXAdditions)
- (id)mutableDeepCopy;
@end
@interface NSData (TSXAdditions)
- (NSString *)hexadecimalString;
- (NSString *)base64EncodedString;
@end

View File

@@ -0,0 +1,239 @@
/*
Additions to Cocoa touch classes
Copyright 2013 Thincast Technologies GmbH, Authors: Dorian Johnson, Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
#import "TSXAdditions.h"
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
@implementation NSObject (TSXAdditions)
- (void)setValuesForKeyPathsWithDictionary:(NSDictionary *)keyedValues
{
for (id keyPath in keyedValues)
[self setValue:[keyedValues objectForKey:keyPath] forKeyPath:keyPath];
}
- mutableDeepCopy
{
if ([self respondsToSelector:@selector(mutableCopyWithZone:)])
return [self mutableCopy];
else if ([self respondsToSelector:@selector(copyWithZone:)])
return [self copy];
else
return [self retain];
}
@end
#pragma mark -
@implementation NSString (TSXAdditions)
#pragma mark Creation routines
+ (NSString *)stringWithUUID
{
CFUUIDRef uuidObj = CFUUIDCreate(nil);
NSString *uuidString = (NSString *)CFUUIDCreateString(nil, uuidObj);
CFRelease(uuidObj);
return [uuidString autorelease];
}
/* Code from
* http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/GTMNSData%2BHex.m?r=344
*/
- (NSData *)dataFromHexString
{
NSData *hexData = [self dataUsingEncoding:NSASCIIStringEncoding];
const char *hexBuf = [hexData bytes];
NSUInteger hexLen = [hexData length];
// This indicates an error converting to ASCII.
if (!hexData)
return nil;
if ((hexLen % 2) != 0)
{
return nil;
}
NSMutableData *binaryData = [NSMutableData dataWithLength:(hexLen / 2)];
unsigned char *binaryPtr = [binaryData mutableBytes];
unsigned char value = 0;
for (NSUInteger i = 0; i < hexLen; i++)
{
char c = hexBuf[i];
if (!isxdigit(c))
{
return nil;
}
if (isdigit(c))
{
value += c - '0';
}
else if (islower(c))
{
value += 10 + c - 'a';
}
else
{
value += 10 + c - 'A';
}
if (i & 1)
{
*binaryPtr++ = value;
value = 0;
}
else
{
value <<= 4;
}
}
return [NSData dataWithData:binaryData];
}
+ (NSString *)hexStringFromData:(const unsigned char *)data
ofSize:(unsigned int)size
withSeparator:(NSString *)sep
afterNthChar:(int)sepnth
{
NSMutableString *result;
NSString *immutableResult;
result = [[NSMutableString alloc] init];
for (int i = 0; i < size; i++)
{
if (i && sep && sepnth && i % sepnth == 0)
[result appendString:sep];
[result appendFormat:@"%02X", data[i]];
}
immutableResult = [NSString stringWithString:result];
[result release];
return immutableResult;
}
@end
#pragma mark Mutable deep copy for dictionary, array and set
@implementation NSDictionary (TSXAdditions)
- mutableDeepCopy
{
NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init];
NSEnumerator *enumerator = [self keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
id obj = [[self objectForKey:key] mutableDeepCopy];
[newDictionary setObject:obj forKey:key];
[obj release];
}
return newDictionary;
}
@end
@implementation NSArray (TSXAdditions)
- mutableDeepCopy
{
NSMutableArray *newArray = [[NSMutableArray alloc] init];
NSEnumerator *enumerator = [self objectEnumerator];
id obj;
while ((obj = [enumerator nextObject]))
{
obj = [obj mutableDeepCopy];
[newArray addObject:obj];
[obj release];
}
return newArray;
}
@end
@implementation NSSet (TSXAdditions)
- mutableDeepCopy
{
NSMutableSet *newSet = [[NSMutableSet alloc] init];
NSEnumerator *enumerator = [self objectEnumerator];
id obj;
while ((obj = [enumerator nextObject]))
{
obj = [obj mutableDeepCopy];
[newSet addObject:obj];
[obj release];
}
return newSet;
}
@end
#pragma mark -
/* Code from
* http://stackoverflow.com/questions/1305225/best-way-to-serialize-a-nsdata-into-an-hexadeximal-string
*/
@implementation NSData (TSXAdditions)
#pragma mark - String Conversion
- (NSString *)hexadecimalString
{
/* Returns hexadecimal string of NSData. Empty string if data is empty. */
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
if (!dataBuffer)
return [NSString string];
NSUInteger dataLength = [self length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i)
[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];
return [NSString stringWithString:hexString];
}
/* Code from http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html */
- (NSString *)base64EncodedString
{
// Construct an OpenSSL context
BIO *context = BIO_new(BIO_s_mem());
// Tell the context to encode base64
BIO *command = BIO_new(BIO_f_base64());
context = BIO_push(command, context);
BIO_set_flags(context, BIO_FLAGS_BASE64_NO_NL);
// Encode all the data
ERR_clear_error();
BIO_write(context, [self bytes], [self length]);
(void)BIO_flush(context);
// Get the data out of the context
char *outputBuffer;
long outputLength = BIO_get_mem_data(context, &outputBuffer);
NSString *encodedString = [[NSString alloc] initWithBytes:outputBuffer
length:outputLength
encoding:NSASCIIStringEncoding];
BIO_free_all(context);
return encodedString;
}
@end

View File

@@ -0,0 +1,60 @@
/***************************************************************************
Toast+UIView.h
Toast
Version 0.1
Copyright (c) 2011 Charles Scalesse.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
***************************************************************************/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface UIView (Toast)
#define ToastDurationLong 5.0
#define ToastDurationNormal 3.0
#define ToastDurationShort 1.0
// each makeToast method creates a view and displays it as toast
- (void)makeToast:(NSString *)message;
- (void)makeToast:(NSString *)message duration:(float)interval position:(id)point;
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
title:(NSString *)title;
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
title:(NSString *)title
image:(UIImage *)image;
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
image:(UIImage *)image;
// the showToast method displays an existing view as toast
- (void)showToast:(UIView *)toast;
- (void)showToast:(UIView *)toast duration:(float)interval position:(id)point;
@end

View File

@@ -0,0 +1,367 @@
/***************************************************************************
Toast+UIView.h
Toast
Version 0.1
Copyright (c) 2011 Charles Scalesse.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
***************************************************************************/
#import "Toast+UIView.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#define kMaxWidth 0.8
#define kMaxHeight 0.8
#define kHorizontalPadding 10.0
#define kVerticalPadding 10.0
#define kCornerRadius 10.0
#define kOpacity 0.8
#define kFontSize 16.0
#define kMaxTitleLines 999
#define kMaxMessageLines 999
#define kFadeDuration 0.2
#define kDefaultLength 3
#define kDefaultPosition @"bottom"
#define kImageWidth 80.0
#define kImageHeight 80.0
static NSString *kDurationKey = @"duration";
@interface UIView (ToastPrivate)
- (CGPoint)getPositionFor:(id)position toast:(UIView *)toast;
- (UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image;
@end
@implementation UIView (Toast)
#pragma mark -
#pragma mark Toast Methods
- (void)makeToast:(NSString *)message
{
[self makeToast:message duration:kDefaultLength position:kDefaultPosition];
}
- (void)makeToast:(NSString *)message duration:(float)interval position:(id)point
{
UIView *toast = [self makeViewForMessage:message title:nil image:nil];
[self showToast:toast duration:interval position:point];
}
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
title:(NSString *)title
{
UIView *toast = [self makeViewForMessage:message title:title image:nil];
[self showToast:toast duration:interval position:point];
}
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
image:(UIImage *)image
{
UIView *toast = [self makeViewForMessage:message title:nil image:image];
[self showToast:toast duration:interval position:point];
}
- (void)makeToast:(NSString *)message
duration:(float)interval
position:(id)point
title:(NSString *)title
image:(UIImage *)image
{
UIView *toast = [self makeViewForMessage:message title:title image:image];
[self showToast:toast duration:interval position:point];
}
- (void)showToast:(UIView *)toast
{
[self showToast:toast duration:kDefaultLength position:kDefaultPosition];
}
- (void)showToast:(UIView *)toast duration:(float)interval position:(id)point
{
/****************************************************
* *
* Displays a view for a given duration & position. *
* *
****************************************************/
CGPoint toastPoint = [self getPositionFor:point toast:toast];
// use an associative reference to associate the toast view with the display interval
objc_setAssociatedObject(toast, &kDurationKey, [NSNumber numberWithFloat:interval],
OBJC_ASSOCIATION_RETAIN);
[toast setCenter:toastPoint];
[toast setAlpha:0.0];
[self addSubview:toast];
[UIView beginAnimations:@"fade_in" context:toast];
[UIView setAnimationDuration:kFadeDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[toast setAlpha:1.0];
[UIView commitAnimations];
}
#pragma mark -
#pragma mark Animation Delegate Method
- (void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
UIView *toast = (UIView *)context;
// retrieve the display interval associated with the view
float interval = [(NSNumber *)objc_getAssociatedObject(toast, &kDurationKey) floatValue];
if ([animationID isEqualToString:@"fade_in"])
{
[UIView beginAnimations:@"fade_out" context:toast];
[UIView setAnimationDelay:interval];
[UIView setAnimationDuration:kFadeDuration];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[toast setAlpha:0.0];
[UIView commitAnimations];
}
else if ([animationID isEqualToString:@"fade_out"])
{
[toast removeFromSuperview];
}
}
#pragma mark -
#pragma mark Private Methods
- (CGPoint)getPositionFor:(id)point toast:(UIView *)toast
{
/*************************************************************************************
* *
* Converts string literals @"top", @"bottom", @"center", or any point wrapped in an *
* NSValue object into a CGPoint *
* *
*************************************************************************************/
if ([point isKindOfClass:[NSString class]])
{
if ([point caseInsensitiveCompare:@"top"] == NSOrderedSame)
{
return CGPointMake(self.bounds.size.width / 2,
(toast.frame.size.height / 2) + kVerticalPadding);
}
else if ([point caseInsensitiveCompare:@"bottom"] == NSOrderedSame)
{
return CGPointMake(self.bounds.size.width / 2,
(self.bounds.size.height - (toast.frame.size.height / 2)) -
kVerticalPadding);
}
else if ([point caseInsensitiveCompare:@"center"] == NSOrderedSame)
{
return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
}
}
else if ([point isKindOfClass:[NSValue class]])
{
return [point CGPointValue];
}
NSLog(@"Error: Invalid position for toast.");
return [self getPositionFor:kDefaultPosition toast:toast];
}
- (UIView *)makeViewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image
{
/***********************************************************************************
* *
* Dynamically build a toast view with any combination of message, title, & image. *
* *
***********************************************************************************/
if ((message == nil) && (title == nil) && (image == nil))
return nil;
UILabel *messageLabel = nil;
UILabel *titleLabel = nil;
UIImageView *imageView = nil;
// create the parent view
UIView *wrapperView = [[[UIView alloc] init] autorelease];
[wrapperView.layer setCornerRadius:kCornerRadius];
[wrapperView setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:kOpacity]];
wrapperView.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
if (image != nil)
{
imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[imageView
setFrame:CGRectMake(kHorizontalPadding, kVerticalPadding, kImageWidth, kImageHeight)];
}
float imageWidth, imageHeight, imageLeft;
// the imageView frame values will be used to size & position the other views
if (imageView != nil)
{
imageWidth = imageView.bounds.size.width;
imageHeight = imageView.bounds.size.height;
imageLeft = kHorizontalPadding;
}
else
{
imageWidth = imageHeight = imageLeft = 0;
}
if (title != nil)
{
titleLabel = [[[UILabel alloc] init] autorelease];
[titleLabel setNumberOfLines:kMaxTitleLines];
[titleLabel setFont:[UIFont boldSystemFontOfSize:kFontSize]];
[titleLabel setTextAlignment:UITextAlignmentLeft];
[titleLabel setLineBreakMode:UILineBreakModeWordWrap];
[titleLabel setTextColor:[UIColor whiteColor]];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setAlpha:1.0];
[titleLabel setText:title];
// size the title label according to the length of the text
CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth,
self.bounds.size.height * kMaxHeight);
CGSize expectedSizeTitle = [title sizeWithFont:titleLabel.font
constrainedToSize:maxSizeTitle
lineBreakMode:titleLabel.lineBreakMode];
[titleLabel setFrame:CGRectMake(0, 0, expectedSizeTitle.width, expectedSizeTitle.height)];
}
if (message != nil)
{
messageLabel = [[[UILabel alloc] init] autorelease];
[messageLabel setNumberOfLines:kMaxMessageLines];
[messageLabel setFont:[UIFont systemFontOfSize:kFontSize]];
[messageLabel setLineBreakMode:UILineBreakModeWordWrap];
[messageLabel setTextColor:[UIColor whiteColor]];
[messageLabel setTextAlignment:UITextAlignmentCenter];
[messageLabel setBackgroundColor:[UIColor clearColor]];
[messageLabel setAlpha:1.0];
[messageLabel setText:message];
// size the message label according to the length of the text
CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * kMaxWidth) - imageWidth,
self.bounds.size.height * kMaxHeight);
CGSize expectedSizeMessage = [message sizeWithFont:messageLabel.font
constrainedToSize:maxSizeMessage
lineBreakMode:messageLabel.lineBreakMode];
[messageLabel
setFrame:CGRectMake(0, 0, expectedSizeMessage.width, expectedSizeMessage.height)];
}
// titleLabel frame values
float titleWidth, titleHeight, titleTop, titleLeft;
if (titleLabel != nil)
{
titleWidth = titleLabel.bounds.size.width;
titleHeight = titleLabel.bounds.size.height;
titleTop = kVerticalPadding;
titleLeft = imageLeft + imageWidth + kHorizontalPadding;
}
else
{
titleWidth = titleHeight = titleTop = titleLeft = 0;
}
// messageLabel frame values
float messageWidth, messageHeight, messageLeft, messageTop;
if (messageLabel != nil)
{
messageWidth = messageLabel.bounds.size.width;
messageHeight = messageLabel.bounds.size.height;
messageLeft = imageLeft + imageWidth + kHorizontalPadding;
messageTop = titleTop + titleHeight + kVerticalPadding;
}
else
{
messageWidth = messageHeight = messageLeft = messageTop = 0;
}
// compare the title & message widths and use the longer value to calculate the size of the
// wrapper width the same logic applies to the x value (left)
float longerWidth = (messageWidth < titleWidth) ? titleWidth : messageWidth;
float longerLeft = (messageLeft < titleLeft) ? titleLeft : messageLeft;
// if the image width is larger than longerWidth, use the image width to calculate the wrapper
// width. the same logic applies to the wrapper height
float wrapperWidth =
((longerLeft + longerWidth + kHorizontalPadding) < imageWidth + (kHorizontalPadding * 2))
? imageWidth + (kHorizontalPadding * 2)
: (longerLeft + longerWidth + kHorizontalPadding);
float wrapperHeight =
((messageTop + messageHeight + kVerticalPadding) < imageHeight + (kVerticalPadding * 2))
? imageHeight + (kVerticalPadding * 2)
: (messageTop + messageHeight + kVerticalPadding);
[wrapperView setFrame:CGRectMake(0, 0, wrapperWidth, wrapperHeight)];
if (titleLabel != nil)
{
[titleLabel setFrame:CGRectMake(titleLeft, titleTop, titleWidth, titleHeight)];
[wrapperView addSubview:titleLabel];
}
if (messageLabel != nil)
{
[messageLabel setFrame:CGRectMake(messageLeft, messageTop, messageWidth, messageHeight)];
[wrapperView addSubview:messageLabel];
}
if (imageView != nil)
{
[wrapperView addSubview:imageView];
}
return wrapperView;
}
@end