Objective c 调整窗口大小时,使缩放视图的内容居中且可见
我试图放大包含Objective c 调整窗口大小时,使缩放视图的内容居中且可见,objective-c,cocoa,nsview,nstextview,Objective C,Cocoa,Nsview,Nstextview,我试图放大包含NSTextView的NSScrollView,并始终将其置于内容的中心。NSTextView具有左/右插入,以保持单词包装的一致性,并将段落很好地保持在视图的中心 [NSScrollView scaleUnitSquareToSize:…]和设置放大率:…都有自己的怪癖和问题,但目前设置放大率似乎是一个更好的选择,因为它不是相对的 下面是发生的事情(还有其他奇怪的事情): 调整大小时,我会更新插图: CGFloat inset = self.textScrollView.fra
NSTextView
的NSScrollView
,并始终将其置于内容的中心。NSTextView
具有左/右插入,以保持单词包装的一致性,并将段落很好地保持在视图的中心
[NSScrollView scaleUnitSquareToSize:…]
和设置放大率:…
都有自己的怪癖和问题,但目前设置放大率似乎是一个更好的选择,因为它不是相对的
下面是发生的事情(还有其他奇怪的事情):
调整大小时,我会更新插图:
CGFloat inset = self.textScrollView.frame.size.width / 2 - _documentWidth / 2;
self.textView.textContainerInset = NSMakeSize(inset, TEXT_INSET_TOP);
self.textView.textContainer.size = NSMakeSize(_documentWidth, self.textView.textContainer.size.height);
放大:
CGFloat magnification = [self.textScrollView magnification];
NSPoint center = NSMakePoint(self.textScrollView.frame.size.width / 2, self.textScrollView.frame.size.height / 2);
if (zoomIn) magnification += .05; else magnification -= .05;
[self.textScrollView setMagnification:magnification centeredAtPoint:center];
每样东西都能工作一段时间。有时,根据窗口从哪个窗口角调整大小,ScrollView会失去中心,我还没有找到一个解决方案来重新调整放大的NSScrollView
视图的中心
放大后,在调整窗口大小时,布局约束也会被打破,尤其是当textContainer
被剪裁出视图时,应用程序会崩溃,并出现以下错误:
***在-[NSISLinearExpression addVariable:covercient:],/Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1349.91/Layout.subproj/IncrementalSimplex/NSISLinearExpression.m:716中断言失败
一个问题可能是,我正在根据UIScrollView
frame size设置插图,因为所包含的NSTextView的坐标在放大后似乎不是相对的,而是绝对的
有没有什么安全的方法可以放大这种视图,并使其始终以内容为中心?为什么我的约束被打破了呢?我也遇到了类似的问题,不幸的是,我最终自己做了定心。以下是我的解决方案的一些亮点
需要递归预防!(否则堆栈溢出:)
创建一个不可绘制的NSView作为documentView,然后将可绘制视图添加为手动居中的子视图,并手动将框架设置为父视图的visibleRect
重写visibleRect,如果它无效,则再次调用它,并进行调试以确保它有效李>
缩放分层视图sux。您可以尝试使用NSTiledLayer,但我已经多次尝试并放弃了该解决方案
代码如下:
@interface FlippedParentView : NSView
@end
@implementation FlippedParentView
- (BOOL) isFlipped { return YES; }
@end
- (void)awakeFromNib
{
[self resetMouseInfo];
[[self window] setAcceptsMouseMovedEvents:YES];
needsFullRedraw = YES;
[self setAcceptsTouchEvents:YES];
// problem: when zoomed-in, CALayer backed NSOpenGLView becomes too large
// and hurts performance.
// solution: create a fullsizeView for the NSScrollView to resize,
// and make NSOpenGLView a subview. Keep NSOpenGLView size the same as visibleRect,
// positioning it as needed on the fullsizeView.
NSScrollView *scrollvw = [self enclosingScrollView];
[scrollvw setBackgroundColor:[NSColor darkStrokeColor]];
fullsizeView = [[FlippedParentView alloc] initWithFrame: [self frame]];
[scrollvw setDocumentView:fullsizeView];
[fullsizeView setAutoresizesSubviews:NO];
//printf("mask %d\n", [self autoresizingMask]);
[fullsizeView setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable | NSViewMinYMargin | NSViewMaxYMargin | NSViewMaxXMargin | NSViewMinXMargin];
[self setAutoresizingMask: NSViewNotSizable];
[fullsizeView addSubview:self];
}
- (NSRect) visibleRect
{
NSRect visRect = [super visibleRect];
if ( visRect.size.width == 0 )
{
visRect = [[self superview] visibleRect];
if ( visRect.size.width == 0 )
{
// this jacks up everything
DUMP( @"bad visibleRect" );
}
visRect.origin = NSZeroPoint;
}
return visRect;
}
- (void) _my_zoom: (double)newZoom
{
mouseFocusPt = [self focusPt];
NSRect oldVisRect = [[self superview] visibleRect];
if ( newZoom < 1.0 )
newZoom = 1.0;
if ( newZoom > kZoomFactorMax ) newZoom = kZoomFactorMax;
float xpct = (mouseFocusPt.x - oldVisRect.origin.x) /
( NSMaxX(oldVisRect) - oldVisRect.origin.x );
float ypct = (mouseFocusPt.y - oldVisRect.origin.y) /
( NSMaxY(oldVisRect) - oldVisRect.origin.y );
float oldZoom = zoomFactor;
zoomFactor = newZoom;
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Stay locked on users' relative mouse location, so user can zoom in and back out without
// the view scrolling out from under the mouse location.
NSPoint newFocusPt = NSMakePoint (mouseFocusPt.x * newZoom/oldZoom,
mouseFocusPt.y * newZoom/oldZoom) ;
NSRect myFrame = fullsizeFrame; // [self frame];
float marginPercent = (myFrame.size.height - drawableSizeWithMargins.height) / drawableSizeWithMargins.height;
[self updateContext];
NSRect newVisRect;
newVisRect.size = [self visibleRect].size;
newVisRect.origin.x = (newFocusPt.x) - (xpct * newVisRect.size.width);
//DLog( @"xpct %0.2f, zoomFactor %0.2f, newVisRect.origin.x %0.2f", xpct, zoomFactor, newVisRect.origin.x);
myFrame = fullsizeFrame; // [self frame];
float marginPercent2 = (myFrame.size.height - drawableSizeWithMargins.height) / drawableSizeWithMargins.height;
float marginDiff = (marginPercent - marginPercent2) * drawableSizeWithMargins.height;
newVisRect.origin.y = (newFocusPt.y ) - (ypct * newVisRect.size.height) - marginDiff;
//DLog( @"ypct %0.2f, zoomFactor %0.2f, newVisRect.origin.y %0.2f", ypct, zoomFactor, newVisRect.origin.y);
//DLog( @"marginPercent %0.2f newVisRect %@", marginPercent, NSStringFromRect(newVisRect) );
if ( newVisRect.origin.x < 1 ) newVisRect.origin.x = 1;
if ( newVisRect.origin.y < 1 ) newVisRect.origin.y = 1;
// NSLog( @"zoom scrollRectToVisible %@ bounds %@", NSStringFromRect(newVisRect), NSStringFromRect([[self superview] bounds]) );
// if ( iUseMousePt || isSlider )
[[self superview] scrollRectToVisible:newVisRect];
}
// - zoomFactor of 1.0 is defined as the zoomFactor needed to show entire selected context within visibleRect,
// including margins of 5% of the context size
// - zoomFactor > 1.0 will make pixels look bigger (view a subsection of a larger total drawableSize)
// - zoomFactor < 1.0 will make pixels look smaller (selectedContext size will be less than drawableSize)
-(void)updateContext
{
static BOOL sRecursing = NO;
if ( sRecursing ) return; // prevent recursion
sRecursing = YES;
//NSRect scrollRect = [[self superview] frame];
NSRect clipViewRect = [[[self enclosingScrollView] contentView] frame];
NSRect visRect = [[self superview] visibleRect]; // careful... visibleRect is sometimes NSZeroRect
float layoutWidth = clipViewRect.size.width;
float layoutHeight = clipViewRect.size.height;
marginPct = layoutHeight / (layoutHeight - (overlayViewMargin*2) );
// Satisfy the constraints fully-zoomed-out case:
// 1) the drawable rect is centered in the view with at margins.
// Allow for 5% margins (1.025 = 2.5% left, right, top, bottom)
// 2) guarantee the drawable rect does not overlap the mini-map in upper right corner.
NSRect baseRect = NSZeroRect;
baseRect.size = visRect.size;
NSRect drawableBaseRect = getCenteredRectFloat(baseRect, metaUnionRect.size );
//drawableSizeWithMargins = nsIntegralSize( nsScaleSize( drawableBaseRect.size, zoomFactor ) );
drawableSizeWithMargins = nsScaleSize( drawableBaseRect.size, zoomFactor );
// drawableSize will NOT include the margins. We loop until we've satisfied
// the constraints above.
drawableSize = drawableSizeWithMargins;
do
{
NSSize shrunkSize;
shrunkSize.width = layoutWidth / marginPct;
shrunkSize.height = layoutHeight / marginPct;
//drawableSize = nsIntegralSize( nsScaleSize( drawableBaseRect.size, zoomFactor / marginPct ));
drawableSize = nsScaleSize( drawableBaseRect.size, zoomFactor / marginPct );
[self calculateMiniMapRect]; // get approx. size. Will calculate once more below.
NSRect shrunkRect = getCenteredRectNoScaling(baseRect, shrunkSize );
// DLog( @"rough miniMapRect %@ shrunk %@", NSStringFromRect(miniMapRect), NSStringFromRect(shrunkRect));
// make sure minimap doesn't overlap drawable when you scroll to top-left
NSRect topMiniMapRect = miniMapRect;
topMiniMapRect.origin.x -= visRect.origin.x;
topMiniMapRect.origin.y = 0;
if ( !NSIntersectsRect( topMiniMapRect, shrunkRect ) )
{
topMarginPercent = fabs(shrunkRect.origin.y - drawableBaseRect.origin.y) / baseRect.size.height;
break;
}
float topMarginOffset = shrunkRect.size.height + (baseRect.size.height * 0.025);
shrunkRect.origin.y = NSMaxY(baseRect) - topMarginOffset;
if ( !NSIntersectsRect( topMiniMapRect, shrunkRect ) )
{
topMarginPercent = fabs(shrunkRect.origin.y - drawableBaseRect.origin.y) / baseRect.size.height;
break;
}
marginPct *= 1.025;
} while (1);
fullsizeFrame.origin = NSZeroPoint;
fullsizeFrame.size.width = fmax(drawableSizeWithMargins.width, layoutWidth);
fullsizeFrame.size.height = fmax(drawableSizeWithMargins.height, layoutHeight);
[fullsizeView setFrame:fullsizeFrame];
NSRect myNewFrame = [fullsizeView visibleRect];
if (myNewFrame.size.width > 0)
[self setFrame: myNewFrame]; //NSView
sRecursing = NO;
}
@接口FlippedParentView:NSView
@结束
@实现FlippedParentView
-(BOOL)被翻转{返回YES;}
@结束
-(无效)从NIB中唤醒
{
[自我重置鼠标信息];
[[self window]setAcceptsMouseMovedEvents:是];
needsFullRedraw=是;
[自我设置接受接触事件:是];
//问题:放大时,CALayer支持的NSOpenGLView变得太大
//而且会影响性能。
//解决方案:为NSScrollView创建一个fullsizeView以调整大小,
//并使NSOpenGLView成为子视图。保持NSOpenGLView大小与visibleRect相同,
//根据需要在fullsizeView上定位它。
NSScrollView*scrollvw=[自封闭CrollView];
[scrollvw setBackgroundColor:[NSColor darkStrokeColor]];
fullsizeView=[[FlippedParentView alloc]initWithFrame:[self frame]];
[scrollvw setDocumentView:fullsizeView];
[fullsizeView SETAUTORESISSUBVIEWS:否];
//printf(“掩码%d\n,[self autoresizingMask]);
[fullsizeView setAutoresizingMask:NSVIEWHEIGHT SIZABLE | NSVIEWWIDTH SIZABLE | NSViewMinYMargin | NSVIEWMAXMARGIN | NSVIEWMAXMARGIN | NSViewMinXMargin];
[self-setAutoresizingMask:nsViewNotSize];
[fullsizeView addSubview:self];
}
-(NSRect)visibleRect
{
NSRect visRect=[super visibleRect];
if(visRect.size.width==0)
{
visRect=[[self superview]visibleRect];
if(visRect.size.width==0)
{
//这把一切都搞砸了
转储(@“坏的visibleRect”);
}
visRect.origin=NSZeroPoint;
}
回归直观;
}
-(无效)\u我的\u缩放:(双)新缩放
{
mouseFocusPt=[自聚焦];
NSRect oldVisRect=[[self superview]visibleRect];
如果(新缩放<1.0)
newZoom=1.0;
如果(newZoom>kZoomFactorMax)newZoom=kZoomFactorMax;
float xpct=(mousefocust.x-oldVisRect.origin.x)/
(NSMaxX(oldVisRect)-oldVisRect.origin.x);
浮点ypct=(mousefocust.y-oldVisRect.origin.y)/
(NSMaxY(oldVisRect)-oldVisRect.origin.y);
float oldZoom=zoomFactor;
zoomFactor=newZoom;
/////////////////////////////////////////////////////////////////////////////////////////////////////
//保持锁定在用户的相对鼠标位置上,这样用户就可以在不使用鼠标的情况下进行放大和缩小
//从鼠标位置下滚动出的视图。
NSPoint newFocusPt=NSMakePoint(mousefocust.x*newZoom/oldZoom,
鼠标焦点y*新缩放/旧缩放);
NSRect myFrame=fullsizeFrame;//[self-frame];
浮动边距百分比=(myFrame.size.height-drawableSizeWithMargins.height)/drawableSizeWithMargins.height;
[自我更新文本];
NSRect newVisRect;
newVisRect.size=[self visibleRect].size;
newVisRect.origin.x=(newFocusPt.x)-(xpct*newVisRect.size.width);
//DLog(@“xpct%0.2f,zoomFactor%0.2f,newVisRect.origin.x%0.2f”,xpct,zoomFactor,newVisRect.origin.x);
myFrame=fullsizeFrame;//[self-frame];
float marginPercent2=(myFrame.size.height-drawableSizeWithMargins.height)/drawableSizeWithMargins.height;
浮动保证金贴现=(保证金百分比-保证金百分比2)*可提取尺寸与保证金高度;
newVisRect.origin.y=(newFocusPt.y)-(ypct*newVisRect.size.height)-marginDiff;
//DLog(@“ypct%0.2f,zoomFactor%0.2f,NewVistrect.origin.y%0.2f”,ypct,zoomFactor,NewVistrect.origin.y);
//DLog(@“marginPercent%0.2f newVisRect%@”,marginPercent,NSStringFromRect(newVisRect));
如果(newVisRect.origin.x<1)newVisRect.origin.x=1;
if(newVisRect.origin.y<1