Ответ 1
Я смог решить это, переопределив wantsDefaultClipping
для подпунктов, чтобы вернуть NO
.
Можно ли сделать NSView
не зацепить его подзоны, которые находятся за пределами границ? На iOS я просто установил clipsToBounds
моего UIView
no NO
. Но NSView
не обладает таким свойством. Я попытался поэкспериментировать с wantsLayer
, masksToBounds
, wantsDefaultClipping
, но все они, похоже, изменяют только обрезку метода drawRect
, а не subviews.
Я смог решить это, переопределив wantsDefaultClipping
для подпунктов, чтобы вернуть NO
.
Если вы используете автозапуск, переопределите alignmentRectInsets
, чтобы развернуть область отсечения, делая ее больше прямоугольника выравнивания. Этот пример дает пространство 50 со всех сторон:
override var alignmentRectInsets: NSEdgeInsets {
return NSEdgeInsets(top: 50.0, left: 50.0, bottom: 50.0, right: 50.0)
}
После 5 часов борьбы я просто добился этого. Просто измените класс любого NSView в .storyboard или .xib на NoClippingView, и он НЕ будет обрезать любые его подпредставления.
class NoClippingLayer: CALayer {
override var masksToBounds: Bool {
set {
}
get {
return false
}
}
}
class NoClippingView: OSView {
override var wantsDefaultClipping: Bool {
return false
}
override func awakeFromNib() {
super.awakeFromNib()
wantsLayer = true
layer = NoClippingLayer()
}
}
Почему я переопределяю masksToBounds в NoClippingLayer? Потому что некоторые собственные классы AppKit изменяют это свойство всех подуровней во время выполнения без каких-либо предупреждений. Например, NSCollectionView делает это для представлений о его ячейках.
Поведение вокруг этого, кажется, изменилось. Вам просто нужно установить слой вида, чтобы не маскировать границы.
view.wantsLayer = true
view.layer?.masksToBounds = false
wantDefaultClipping метод работает... и родитель, и ребенок должны override хочетDefaultClipping.
Как только вы идете по этому маршруту, очистка после себя громоздка.
Здесь мое решение для прикрепления текста к NSProgressBar:
Благодаря BGHUDAppKit и stackoverflow
@implementation BGHUDOverlayTextField
- (BOOL) wantsDefaultClipping { return NO; } // thanks stackoverflow.com
@end
@interface BGHUDProgressIndicator : NSProgressIndicator {
NSBezierPath *progressPath;
NSString *themeKey;
NSString *LayerKey;
BGHUDOverlayTextField *customTitleField;
BOOL hiding;
}
@implementation BGHUDProgressIndicator
- (BOOL) wantsDefaultClipping { return (customTitleField == nil); } // thanks stackoverflow.com
- (void) setCustomTitle: (NSMutableAttributedString *)iTitle
{
if ( !customTitleField )
{
NSRect r = [self bounds];
r.size.height += 10;
customTitleField = [[BGHUDOverlayTextField alloc] initWithFrame:r];
[self addSubview: customTitleField];
}
[customTitleField setAttributedStringValue:iTitle];
}
- (void)setHidden:(BOOL)flag
{
if ( customTitleField && flag && !hiding )
{
hiding = YES;
NSRect fr = [self bounds];
NSRect eraseFr = fr;
eraseFr.size = [customTitleField frame].size;
[self displayRectIgnoringOpacity:fr];
}
else if ( !flag )
hiding = NO;
[super setHidden:flag];
}
- (void) drawRect: (NSRect)fr
{
if ( customTitleField )
{
fr = [self bounds];
NSRect eraseFr = fr;
eraseFr.size = [customTitleField frame].size;
[[NSColor normalSolidFill] set];
NSRectFill( eraseFr );
if ( hiding )
{
[customTitleField setAttributedStringValue:nil];
NSRectFill( eraseFr );
return;
}
[NSGraphicsContext saveGraphicsState];
NSRectClip(fr);
}
[super drawRect:fr];
if ( customTitleField )
{
[NSGraphicsContext restoreGraphicsState];
}
}
Для людей, ищущих эквивалентный быстрый код:
Добавьте этот код в подкласс NSView:
override var wantsDefaultClipping:Bool{return false}//avoids clipping the view
Вам не нужно менять родителя. пока родитель имеет достаточно большую область отсечения. Но его удобно также добавить эту переменную к родительскому.
Это непростая тема, и я провел недели, исследуя это, и мое лучшее решение можно найти здесь: http://stylekit.org/blog/2015/12/24/The-odd-case-of-luck/
Чтобы увидеть приведенную выше концепцию в действии, проверьте эту статью: http://stylekit.org/blog/2015/12/30/Graphic-framework-for-OSX/
Я не мог заставить любое из этих решений работать. Я думаю, вам, возможно, придется просто изменить иерархию представлений, чтобы представления не рисовали вне рамки. Вы можете создать промежуточное представление, которое не делает никакого рисунка, но имеет более крупный фрейм, чтобы обеспечить большую область.