gpt4 book ai didi

objective-c - 如何管理 NSView 中自定义数据(不是照片)的绘制,该 NSView 的区域动态大于可视区域?

转载 作者:行者123 更新时间:2023-12-03 17:29:52 42 4
gpt4 key购买 nike

更新:相对于下面的问题和答案,我似乎对 NSView 类与我试图绘制的自定义类有误解,包装NSScrollView。最后,我想要弄清楚的是如何在具有以下功能的 NSView 中管理自定义数据(不是照片)的动态绘制面积大于可视面积?

我不是在寻找讲义,但我是 Cocoa 的新手,我认为我正在根据 Apple 的文档进行最佳实践,但似乎我的基本原理是错误的。苹果的文档非常详细、技术性强,完全以照片处理为中心,因此对我来说毫无用处。 Apple 提供的相关代码示例(例如 Sketch)以典型的倾斜方式从打印机纸张尺寸中获取文档尺寸,但这不是我需要的。我在网上搜索了教程、示例等,但没有找到太多东西(我保证在解决这个问题后会写一个)。

我从 REALbasic 移植此代码,即使使用撤消命令,我也能完全工作,但这样做的范例完全不同。这对我来说并不是“点击”。我很感谢所提供的帮助,但我仍然缺少一些东西,感谢人们提供的任何其他内容。

谢谢

<小时/>

我有一个 NSView 子类,我正在其中创建一个钢琴卷帘 MIDI 接口(interface)。我正在尝试解决一些问题:

  • 在滚动期间和之后绘制伪像
  • 滚动期间和滚动后线条未跨越可见区域
  • 滚动时(有时在 mouseDown 时),水平滚动条会向右跳 1(一)个像素,但我还没有在任何地方实现scrollToPoint。

与上述相关的症状:

  • 实现 adjustScroll 会使一切变得更糟。
  • mouseDown 纠正了所有问题,除了有时向右跳转 1 像素的问题。
  • 如果我取消 NSLog 命令的注释,则不会绘制任何内容。

Apple 的文档提到了像素精确绘图,但(当然)没有提供如何实现这一点的示例。我一直在使用 Floor() 函数来尝试获得一致的值,但是一旦我开始添加scrollToPoint 或任何其他复杂性,事情就会变得困惑。

请参阅链接图像作为示例。如果你相信的话,屏幕截图实际上清理了我在屏幕上看到的内容。几乎到处都有一半不透明度的双线。这同样适用于我绘制的任何对象。

Graphics Artifacts and inconsistencies in a subclassed NSView generated after scrolling http://www.oatmealandcoffee.com/external/NSViewArtifacts.png

这是代码。我讨厌公开放弃这么多,但我到处寻找线索,如果互联网有任何迹象,我是唯一有这个问题的人,我真的只想解决这个问题并继续前进。还有很多,而且还会有更多,但这些是我真正需要正确处理的核心内容,坦率地说,我不知道如何纠正它。

    - (void)drawRect:(NSRect)rect {
//NSLog(@"OCEditorView:drawRect: START");

[self setFrame:[[self EditorDocument] DocumentRect]];

[[NSGraphicsContext currentContext] setShouldAntialias:NO];

// CLEAR BACKGROUND

[[[self EditorDocument] ColorWhiteKey] set];
NSRectFill(rect);

// BACKGROUND KEYS

int firstRowLine = 0; //NSMinY(rect); //<- adding the function results in bad spacing on scrolling
int currentRowLine = 0;
int lastRowLine = NSMaxY(rect);

//NSLog(@"lastRowLine:%d", lastRowLine);

float currentZoomY = [self ZoomY];

for (currentRowLine = firstRowLine; currentRowLine <= lastRowLine; currentRowLine += currentZoomY) {

int currentTone = floor(currentRowLine / [self ZoomY]);
BOOL isBlackKey = [[self MusicLib] IsBlackKey:currentTone];

//NSLog(@"%d, tone:%d, black:%d", [self MusicLib], currentTone, isBlackKey);

if (isBlackKey) {
[[[self EditorDocument] ColorBlackKey] set];
} else {
[[NSColor whiteColor] set];
}

NSBezierPath *rowLine = [NSBezierPath bezierPath];

NSPoint bottomLeftPoint = NSMakePoint(NSMinX(rect), currentRowLine);
NSPoint bottomRightPoint = NSMakePoint(NSMaxX(rect), currentRowLine);
NSPoint topRightPoint = NSMakePoint(NSMaxX(rect), currentRowLine + [self ZoomY]);
NSPoint topLeftPoint = NSMakePoint(NSMinX(rect), currentRowLine + [self ZoomY]);

[rowLine moveToPoint:bottomLeftPoint];
[rowLine lineToPoint:bottomRightPoint];
[rowLine lineToPoint:topRightPoint];
[rowLine lineToPoint:topLeftPoint];

[rowLine closePath];

[rowLine fill];

BOOL isOctave = [[self MusicLib] IsOctave:currentTone];
if (isOctave) {
[[[self EditorDocument] ColorXGrid] set];

NSBezierPath *octaveLine = [NSBezierPath bezierPath];
NSPoint leftPoint = NSMakePoint(NSMinX(rect), currentRowLine);
NSPoint rightPoint = NSMakePoint(NSMaxX(rect), currentRowLine);
[octaveLine moveToPoint:leftPoint];
[octaveLine lineToPoint:rightPoint];
[octaveLine stroke];
}
}

// BACKGROUND MEASURES

//[[self EditorDocument].ColorYGrid setStroke];

int firstColumnLine = 0;
int currentColumnLine = 0;
int lastColumnLine = NSMaxX(rect);

int snapToValueInBeats = [[self EditorDocument] SnapToValue];
int snapToValueInPixels = floor(snapToValueInBeats * [self ZoomX]);
int measureUnitInBeats = floor([[self EditorDocument] TimeSignatureBeatsPerMeasure] * [[self EditorDocument] TimeSignatureBasicBeat]);
int measureUnitInPixels = floor(measureUnitInBeats * [self ZoomX]);

for (currentColumnLine = firstColumnLine; currentColumnLine <= lastColumnLine; currentColumnLine += snapToValueInPixels) {

//int currentBeat = floor(currentColumnLine / [self ZoomX]);
int isAMeasure = currentColumnLine % measureUnitInPixels;
int isAtSnap = currentColumnLine % snapToValueInPixels;

if ((isAMeasure == 0) || (isAtSnap == 0)) {

if (isAtSnap == 0) {
[[NSColor whiteColor] set];
}

if (isAMeasure == 0) {
[[[self EditorDocument] ColorXGrid] set];
}

NSBezierPath *columnLine = [NSBezierPath bezierPath];

NSPoint startPoint = NSMakePoint(currentColumnLine, NSMinY(rect));
NSPoint endPoint = NSMakePoint(currentColumnLine, NSMaxY(rect));

[columnLine moveToPoint:startPoint];
[columnLine lineToPoint:endPoint];

[columnLine setLineWidth:1.0];
[columnLine stroke];

} // isAMeasure or isAtSnap
} // currentColumnLine

// NOTES

for (OCNoteObject *note in [[self EditorDocument] Notes]) {

OCNoteObject *currentNote = note;

NSRect noteBounds = [self GetRectFromNote:currentNote];
//NSLog(@"noteBounds:%d", noteBounds);

// set the color for the note fill
// this will have to come from the parent Track

NSMutableArray *trackColors = [self EditorDocument].TrackColors;

if (note.Selected) {
[[trackColors objectAtIndex:0] set];
} else {
[[trackColors objectAtIndex:1] set];
}

[NSBezierPath fillRect:noteBounds];

// outline

[[NSColor blackColor] set];
[NSBezierPath strokeRect:noteBounds];

} // for each note

/*
if (EditorController.startingUpApplication == YES) {
[self setDefaultSettingForApplicationStartUp];
}
*/
//NSLog(@"OCEditorView:drawRect: END");
}

- (void)mouseDown:(NSEvent *)theEvent {

//NSLog(@"OCEditorObject:mouseDown: START");

// This converts the click into coordinates
MouseDownPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];

// Calculate the beat and pitch clicked into...

float startBeat = floor(MouseDownPoint.x / [self ZoomX]);
float pitch = floor(MouseDownPoint.y / [self ZoomY]);
float length = [[self EditorDocument] NewNoteLength];

//NSLog(@"X:%f, Y:%f", MouseDownPoint.x, MouseDownPoint.y);
//NSLog(@"beat:%f, pitch:%f", startBeat, pitch);

LastDragPoint = MouseDownPoint; // save the point just in case.

OCNoteObject *note = [self GetClickedNoteFromPoint:MouseDownPoint];

if ([EditorController EditorMode] == AddObjectMode) {

//NSLog(@"AddObjectMode)");

float snapToX = [[self EditorDocument] SnapToValue];
float snappedStartBeat = floor(startBeat / snapToX) * snapToX;

//NSLog(@"%f = %f / %f * %f", snappedStartBeat, startBeat, snapToX, snapToX);

OCNoteObject *newNote = [[self EditorDocument] CreateNote:snappedStartBeat Pitch:pitch Length:length];
//NSLog(@"newNote:%d", newNote);

[newNote Deselect];

} else if ([EditorController EditorMode] == EditObjectMode) {

//NSLog(@"EditObjectMode");

// if nothing was clicked, then clear the selections
// else if the shift key was pressed, add to the selection

if (note == nil) {
[self SelectNone];
} else {

//NSLog(@"mouseDown note.pitch:%f, oldPitch:%f", note.Pitch, note.OldPitch);

BOOL editingSelection = (([theEvent modifierFlags] & NSShiftKeyMask) ? YES : NO);
if (editingSelection) {
if (note.Selected) {
[self RemoveFromSelection:note];
} else {
[self AddToSelection:note];
}
} else {
if (note.Selected) {
// do nothing
} else {
[self SelectNone];
[self AddToSelection:note];
}
}

[self SetOldData];

} // (note == nil)

} else if ([EditorController EditorMode] == DeleteObjectMode) {

if (note != nil) {
[self RemoveFromSelection:note];
[[self EditorDocument] DestroyNote:note];
} // (note != nil)

} // EditorMode

[self setFrame:[[self EditorDocument] DocumentRect]];
[self setNeedsDisplay:YES];
}

- (void)mouseDragged:(NSEvent *)theEvent {
//NSLog(@"mouseDragged");

NSPoint currentDragPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
// NSLog(@"currentDragPoint: %d", currentDragPoint)

float snapToValueInBeats = [[self EditorDocument] SnapToValue];

int deltaXinPixels = floor(currentDragPoint.x - MouseDownPoint.x);
int deltaYinPixels = floor(currentDragPoint.y - MouseDownPoint.y);

int deltaXinBeats = floor(deltaXinPixels / [self ZoomX]);
int deltaY = floor(deltaYinPixels / [self ZoomY]);

int deltaX = floor(deltaXinBeats / snapToValueInBeats) * snapToValueInBeats;

for (OCNoteObject *note in [self Selection]) {
[self MoveNote:note DeltaX:deltaX DeltaY:deltaY];
}

LastDragPoint = currentDragPoint;

[self autoscroll:theEvent];

[self setNeedsDisplay:YES]; //artifacts are left if this is off.
}

- (void)mouseUp:(NSEvent *)theEvent {
if ([EditorController EditorMode] == AddObjectMode) {

} else if ([EditorController EditorMode] == EditObjectMode) {

} else if ([EditorController EditorMode] == DeleteObjectMode) {

}

[self setNeedsDisplay:YES];
}

我很可能会遗漏一些明显的东西,但我认为我太接近代码了,无法看到它的解决方案。任何帮助是极大的赞赏!谢谢!

最佳答案

我认为您误解了 drawRect: 及其参数的工作方式:

当你的 View 或它的一部分需要重绘时,消息drawRect:由cocoa发送。 CGRect 参数是当前重绘的所有更新区域的边界框。这意味着您不应从该矩形导出 View 内对象的任何位置。它仅传递给方法以允许优化绘制:如果某些内容完全超出此矩形,则不需要重新绘制。

您应该根据 View 坐标系计算 View 内的所有位置:[selfbounds]。每次执行 drawRect: 时,它都不会改变,并为您提供 View 内容的原点和大小。

您的代码还有一些其他问题(例如,不要从 drawRect: 中调用 setFrame:),但我认为您应该首先获取坐标正确,然后进一步研究如何计算矩形的像素对齐坐标。

关于objective-c - 如何管理 NSView 中自定义数据(不是照片)的绘制,该 NSView 的区域动态大于可视区域?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1551130/

42 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com