更新:相对于下面的问题和答案,似乎我NSView对与我试图绘制的自定义类和包装有关的类有误解NSScrollView.最后,我想弄清楚的是如何在面积大于可视区域的情况下管理自定义数据(而不是照片)的动态绘制NSView?
我不是在寻找讲义,但我是Cocoa的新手,我认为我在Apple的文档上做了最好的练习,但似乎我已经弄错了基础知识.Apple的文档非常详细,技术性很强,完全围绕着处理照片,因此对我来说毫无用处.Apple提供的相关代码示例(例如Sketch)以典型的倾斜方式从打印机纸张尺寸中获取文档大小,这不是我需要的.我已经在网上搜索了教程,示例等,但是我找不到任何东西(我保证在我解决这个问题时写一个).
我正在从REALbasic移植这个代码,我完全使用它,即使使用撤消命令,但这样做的范例完全不同.这对我来说不是"点击".我很欣赏给予的帮助,我在这里仍然遗漏了一些东西,人们提供的其他任何东西都值得赞赏.
谢谢
我有一个子类NSView,我正在创建一个钢琴卷MIDI接口.我想解决一些问题:
与上述有关的症状:
Apple的文档提到像素精确的绘图,但(当然)没有提供如何实现这一点的例子.我一直在使用floor()函数来尝试获得一致的值,但是一旦我开始使用scrollToPoint或任何其他复杂性,事情就会变得混乱.
请以链接图像为例进行说明.屏幕截图,如果您能相信它,实际上清理了我在屏幕上看到的内容.在半透明度下,几乎所有地方都有双线.同样适用于我绘制的任何对象.
在滚动http://www.oatmealandcoffee.com/external/NSViewArtifacts.png后生成的子类NSView中的图形工件和不一致性
这是代码.我讨厌公开放弃这么多,但我到处寻找线索,如果互联网有任何迹象表明我是唯一有这个问题的人,我真的只想把这个问题整理好并继续前进.有很多,还有更多,但这些是我真正需要做的核心事情,坦率地说,我对如何纠正它感到茫然.
- (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];
}
Run Code Online (Sandbox Code Playgroud)
我很可能会遗漏一些明显的东西,但我认为我太接近代码,无法看到它的解决方案.任何帮助是极大的赞赏!谢谢!
我认为你误解了方式drawRect:及其论证的作用:
drawRect:只要您的视图或部分视图需要重绘,cocoa就会发送消息.CGRect参数是当前重绘的所有更新区域的边界框.这意味着您不应从此矩形派生视图中任何对象的位置.它只传递给方法以允许优化绘图:如果某些内容完全在此矩形之外,则不需要重绘.
您应该从视图坐标系计算视图中的所有位置:[self bounds].每次drawRect:执行时都不会更改,并为您提供视图内容的原点和大小.
您的代码还有其他一些问题(例如,不要setFrame:从内部调用drawRect:)但我认为您应首先获得正确的坐标,然后进一步研究如何计算矩形的像素对齐坐标.