• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Vertically-expanding NSTextField
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Vertically-expanding NSTextField


  • Subject: Re: Vertically-expanding NSTextField
  • From: Sebastian Morsch <email@hidden>
  • Date: Sun, 18 Jun 2006 18:02:17 +0200

Dear Andrew,

I was reading this thread because I was looking for a solution for many views to rearrange in interaction to each other. For example, one textfields gets bigger because the user types in something and two other textfields below move downwards accordingly to make room for it.

Thank you for your code on cocoadev, it was very helpful to write the automatically expanding textfield! But still I couldn't get other views to react and move around (which of course wasn't your goal at all).

So I wrote this protocol to define a system with the following behaviour: "if a view changes frame, it will rearrange other views to maintain defined distances between the borders".

The implementation works quite nicely. HOWEVER, this code is veeeery ruff and newbieish, not very well tested and most probably not very elegant. I decided to post it anyway for those who like it and especially for those who don't like it and maybe would like to improve it. I apologize for the code length! As I said, it still lacks elegancy and needs improvement.

If someone thinks, that this should go into another or it's own thread, please let me know!

Thanks!
Sebastian



//  START OF SMViewLinking.h

// protocol that views can adopt to maintain their layout as it was defined in IB.
// one view (source view) can 'link' one of its four graphical borders (= fix the distance) to those of other
// views (destination views), and those destination views will be moved/resized if the
// source view changes.
// if the destination views are source views themselves, the linked destiantion views
// resize, too. this of course, is the big weakness of this system, because...:
//
// WARNING: you have to avoid 'linkining feedbacks' where subviews are stiffly linked
// with their superviews in a closed loop. this conflicts with the standard autoresizing!



#import <Cocoa/Cocoa.h>


// constants typedef enum { SMViewLinkingTopBorderType = 1, SMViewLinkingBottomBorderType = 2, SMViewLinkingLeftBorderType = 4, SMViewLinkingRightBorderType = 8 } SMViewLinkingBorderType;

typedef enum {
	SMViewLinkingResizesHorizontally = 1,
	SMViewLinkingResizesVertically = 2,
	SMViewLinkingConstrainedToMinWidth = 4,
	SMViewLinkingConstrainedToMinHeight = 8,
	SMViewLinkingConstrainedToMaxWidth = 16,
	SMViewLinkingConstrainedToMaxHeight = 32,
} SMViewLinkingLinkedResizingMask;

extern NSString *SMViewLinkingDestViewKeyName;
extern NSString *SMViewLinkingSourceBorderKeyName;
extern NSString *SMViewLinkingDestBorderKeyName;
extern NSString *SMViewLinkingDistanceKeyName;


@protocol SMViewLinking
- (void)linkBorder:(SMViewLinkingBorderType)sourceBorder toView: (NSView *)destView
border:(SMViewLinkingBorderType)destBorder;
- (void)unlinkView:(NSView *)destView;
- (void)moveLinkedBorder:(SMViewLinkingBorderType)border by:(float) offset;
- (SMViewLinkingLinkedResizingMask)linkedResizingMask;
- (void)setLinkedResizingMask:(SMViewLinkingLinkedResizingMask)mask;
- (NSSize)linkedMinSize;
- (void)setLinkedMinSize:(NSSize)minSize;
- (NSSize)linkedMaxSize;
- (void)setLinkedMaxSize:(NSSize)maxSize;
@end



// helper function
float SMViewLinkingBorderPosition(NSRect frame, SMViewLinkingBorderType border)
{
if (border == SMViewLinkingTopBorderType) {return (frame.origin.y + frame.size.height);}
if (border == SMViewLinkingBottomBorderType) {return (frame.origin.y);}
if (border == SMViewLinkingLeftBorderType) {return (frame.origin.x);}
if (border == SMViewLinkingRightBorderType) {return (frame.origin.x + frame.size.width);}
return 0.0;
}


// END OF SMViewLinking.h

------------------------------------------------------------------------ ---

//  START OF SMViewLinking.m


#import "SMViewLinking.h"


// constants
NSString *SMViewLinkingDestViewKeyName = @"SMViewLinkingDestViewKeyName";
NSString *SMViewLinkingSourceBorderKeyName = @"SMViewLinkingSourceBorderKeyName";
NSString *SMViewLinkingDestBorderKeyName = @"SMViewLinkingDestBorderKeyName";
NSString *SMViewLinkingDistanceKeyName = @"SMViewLinkingDistanceKeyName";


// END OF SMViewLinking.m

------------------------------------------------------------------------ ---

//  START OF SMLinkedView.h


#import <Cocoa/Cocoa.h> #import "SMViewLinking.h"


@interface SMLinkedView : NSView <SMViewLinking> { NSMutableArray *linkedViews; SMViewLinkingLinkedResizingMask linkedResizingMask; NSSize linkedMinSize, linkedMaxSize; } @end

// END OF SMLinkedView.h

------------------------------------------------------------------------ ---

//  START OF SMLinkedView.m


#import "SMLinkedView.h"


// private methods
@interface SMLinkedView (private)
- (void)_updateLinkedViews;
- (void)_moveBorder:(SMViewLinkingBorderType)border ofView:(NSView *) view by:(float)offset;
@end



@implementation SMLinkedView

- (id)initWithFrame:(NSRect)frameRect
{
	if (self == [super initWithFrame:frameRect]) {
		linkedViews = [[NSMutableArray alloc] initWithCapacity:0];
		linkedResizingMask = 0;
		linkedMinSize = NSMakeSize(0.0, 0.0);
		linkedMaxSize = NSMakeSize(0.0, 0.0);
	}
	return self;
}

- (id)initWithCoder:(NSCoder *)decoder
{
	if (self == [super initWithCoder:decoder]) {
		linkedViews = [[NSMutableArray alloc] initWithCapacity:0];
		linkedResizingMask = 0;
		linkedMinSize = NSMakeSize(0.0, 0.0);
		linkedMaxSize = NSMakeSize(0.0, 0.0);
	}
	return self;
}

- (void)dealloc
{
	[linkedViews dealloc];
	[super dealloc];
}


// SMViewLinking protocol methods

- (void)linkBorder:(SMViewLinkingBorderType)sourceBorder toView: (NSView *)destView
border:(SMViewLinkingBorderType)destBorder
{
// does the dest view conforms to the SMViewLinking protocol?
if ([[destView class] conformsToProtocol:@protocol(SMViewLinking)] == NO) {return;}


// determine the current distance
NSRect sourceFrame = [[self superview] convertRect:[self frame] toView:nil];
// TODO: better (faster ?) transformation!!!
NSRect destFrame = [[destView superview] convertRect:[destView frame] toView:nil];
// TODO: better (faster ?) transformation!!!
float distance = SMViewLinkingBorderPosition(destFrame, destBorder)
- SMViewLinkingBorderPosition(sourceFrame, sourceBorder);

// add to the linkedViews array
NSDictionary *linkedViewDict = [NSDictionary dictionaryWithObjectsAndKeys:
destView, SMViewLinkingDestViewKeyName,
[NSNumber numberWithInt:sourceBorder], SMViewLinkingSourceBorderKeyName,
[NSNumber numberWithInt:destBorder], SMViewLinkingDestBorderKeyName,
[NSNumber numberWithFloat:distance], SMViewLinkingDistanceKeyName,
nil];
[linkedViews addObject:linkedViewDict];
}


- (void)unlinkView:(NSView *)destView
{
int i;
NSDictionary * linkedViewDict;

for (i = [linkedViews count] - 1; i--; i < 0) {
linkedViewDict = [linkedViews objectAtIndex:i];
if ([linkedViewDict objectForKey:SMViewLinkingDestViewKeyName] == destView) {
[linkedViews removeObjectAtIndex:i];
}
}
}


- (void)moveLinkedBorder:(SMViewLinkingBorderType)border by:(float) offset
{
NSRect frame = [self frame];

// move the border but consider resizing mask
if (border == SMViewLinkingTopBorderType) {
if (linkedResizingMask & SMViewLinkingResizesVertically) {
frame.size.height += offset;
} else {
frame.origin.y += offset;
}
}
if (border == SMViewLinkingBottomBorderType) {
frame.origin.y += offset;
if (linkedResizingMask & SMViewLinkingResizesVertically) {
frame.size.height -= offset;
}
}
if (border == SMViewLinkingLeftBorderType) {
frame.origin.x += offset;
if (linkedResizingMask & SMViewLinkingResizesHorizontally) {
frame.size.width -= offset;
}
}
if (border == SMViewLinkingRightBorderType) {
if (linkedResizingMask & SMViewLinkingResizesHorizontally) {
frame.size.width += offset;
} else {
frame.origin.x += offset;
}
}

// check min/max size
if ((linkedResizingMask & SMViewLinkingConstrainedToMinWidth)
&& (frame.size.width < linkedMinSize.width))
{
frame.size.width = linkedMinSize.width;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMinHeight)
&& (frame.size.height < linkedMinSize.height))
{
frame.size.height = linkedMinSize.height;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMaxWidth)
&& (frame.size.width > linkedMaxSize.width))
{
frame.size.width = linkedMaxSize.width;
}
if ((linkedResizingMask & SMViewLinkingConstrainedToMaxHeight)
&& (frame.size.height > linkedMaxSize.height))
{
frame.size.height = linkedMaxSize.height;
}

[self setFrame:frame];
[[self superview] setNeedsDisplay:YES];
}


- (SMViewLinkingLinkedResizingMask)linkedResizingMask {
	return linkedResizingMask;
}

- (void)setLinkedResizingMask:(SMViewLinkingLinkedResizingMask)mask {
	linkedResizingMask = mask;
}

- (NSSize)linkedMinSize {
	return linkedMinSize;
}

- (void)setLinkedMinSize:(NSSize)minSize {
	linkedMinSize = minSize;
}

- (NSSize)linkedMaxSize {
	return linkedMaxSize;
}

- (void)setLinkedMaxSize:(NSSize)maxSize {
	linkedMaxSize = maxSize;
}


// overridden and private methods

- (void)setFrame:(NSRect)frameRect
{
	[super setFrame:frameRect];
	[self _updateLinkedViews];
}

- (void)_updateLinkedViews
{
// loop thru all linked views and see if they have to change
NSEnumerator *enumerator = [linkedViews objectEnumerator];
NSDictionary *linkedViewDict;
id destView;
NSRect sourceFrame = [[self superview] convertRect:[self frame] toView:nil];
// TODO: better (faster ?) transformation!!!
NSRect destFrame;
SMViewLinkingBorderType sourceBorder, destBorder;
float linkedDistance, actualDistance;

while (linkedViewDict = [enumerator nextObject])
{
destView = [linkedViewDict objectForKey:SMViewLinkingDestViewKeyName];
destFrame = [[destView superview] convertRect:[destView frame] toView:nil];
// TODO: better (faster ?) transformation!!!
sourceBorder = [[linkedViewDict objectForKey:SMViewLinkingSourceBorderKeyName] intValue];
destBorder = [[linkedViewDict objectForKey:SMViewLinkingDestBorderKeyName] intValue];
linkedDistance = [[linkedViewDict objectForKey:SMViewLinkingDistanceKeyName] floatValue];

actualDistance = SMViewLinkingBorderPosition(destFrame, destBorder)
- SMViewLinkingBorderPosition(sourceFrame, sourceBorder);

if (actualDistance != linkedDistance) {
[destView moveLinkedBorder:destBorder by:(linkedDistance - actualDistance)];
}
}
}


@end

// END OF SMLinkedView.m









Am 13.05.2006 um 12:26 schrieb Andrew Bowman:

Okay, I've been pounding away at this for a while and have a working solution posted at CocoaDev.

http://www.cocoadev.com/index.pl?IFVerticallyExpandingTextfield

Take a look!  It mimics iChat's input field quite well.

Comments and suggestions welcome.

- Andrew Bowman
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


_______________________________________________ Do not post admin requests to the list. They will be ignored. Cocoa-dev mailing list (email@hidden) Help/Unsubscribe/Update your Subscription: This email sent to email@hidden
  • Follow-Ups:
    • Re: Vertically-expanding NSTextField
      • From: Andrew Bowman <email@hidden>
  • Prev by Date: Re: Trouble with sheets...
  • Next by Date: Re: Bindings + Trapping Changes
  • Previous by thread: Re: How to make a dictionary for Dictionary.app
  • Next by thread: Re: Vertically-expanding NSTextField
  • Index(es):
    • Date
    • Thread