Zaokrąglij dwa rogi w UIView

83

Niedawno opublikowałem pytanie dotyczące zaokrąglania tylko dwóch rogów widoku i otrzymałem świetną odpowiedź, ale mam problemy z jej wdrożeniem. Oto moja metoda drawRect:

- (void)drawRect:(CGRect)rect {
    //[super drawRect:rect]; <------Should I uncomment this?
    int radius = 5;
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextBeginPath(context);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    CGContextClosePath(context);
    CGContextClip(context);
}

Metoda jest wywoływana, ale nie wydaje się wpływać na wynik widoku. Jakieś pomysły, dlaczego?

Jumhyn
źródło

Odpowiedzi:

104

CACornerMask wprowadzony w iOS 11, który pomaga zdefiniować górną lewą, górną, prawą, dolną lewą, dolną prawą warstwę widoku. Poniżej znajduje się przykład do wykorzystania.

Tutaj staram się zaokrąglić tylko dwa górne rogi:

myView.clipsToBounds = true
myView.layer.cornerRadius = 10
myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]

FYI Ref:

SachinVsSachin
źródło
To wygląda na idiomatyczny sposób na zrobienie tego teraz. Dobry chwyt!
Jumhyn
3
Zwróć uwagę, że szerokość / wysokość widoku nadal musi być co najmniej dwukrotnie większa od promienia narożnika, w przeciwnym razie pojawiają się dziwne artefakty. np. zaokrąglenie tylko dolnych rogów, promień 20 i wysokość tylko 30 powoduje, że komórka jest wyraźnie węższa (o jeden lub dwa piksele).
Graham Perks
Zauważ, że jest to poprawny sposób, jeśli twój widok zmienia swoją ramkę (możliwe dzięki rozwijaniu UITextView, jak w moim przypadku - stackoverflow.com/questions/50926215/… ). Jeśli ustawisz UIBezierPath, nie zaktualizuje się ona automatycznie po zmianie ramki.
thomers
Zauważono, że jest dostępny tylko od iOS 11 i nowszychif #available(iOS 11.0, *) { }
Tri Ngo Minh
2
Świetna odpowiedź. Okazuje się, ustawiając clipsToBoundssię truenie jest konieczne. Możesz zamaskować rogi i dodać cień do warstwy, gdy clipsToBoundsjest false. Przydał się dla mnie.
Au Ris
72


o ile wiem, jeśli chcesz również zamaskować podwidoki, możesz użyć CALayermaskowania. Można to zrobić na dwa sposoby. Pierwsza jest nieco bardziej elegancka, druga to obejście :-), ale jest też szybka. Obie opierają się na CALayermaskowaniu. Użyłem obu metod w kilku projektach w zeszłym roku i mam nadzieję, że znajdziesz coś pożytecznego.

Rozwiązanie 1

Przede wszystkim utworzyłem tę funkcję, aby wygenerować maskę obrazu w locie ( UIImage) z zaokrąglonym rogiem, którego potrzebuję. Ta funkcja zasadniczo wymaga 5 parametrów: granic obrazu i 4 promień narożnika (lewy górny, prawy górny, lewy dolny i prawy dolny).


static inline UIImage* MTDContextCreateRoundedMask( CGRect rect, CGFloat radius_tl, CGFloat radius_tr, CGFloat radius_bl, CGFloat radius_br ) {  

    CGContextRef context;
    CGColorSpaceRef colorSpace;

    colorSpace = CGColorSpaceCreateDeviceRGB();

    // create a bitmap graphics context the size of the image
    context = CGBitmapContextCreate( NULL, rect.size.width, rect.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast );

    // free the rgb colorspace
    CGColorSpaceRelease(colorSpace);    

    if ( context == NULL ) {
        return NULL;
    }

    // cerate mask

    CGFloat minx = CGRectGetMinX( rect ), midx = CGRectGetMidX( rect ), maxx = CGRectGetMaxX( rect );
    CGFloat miny = CGRectGetMinY( rect ), midy = CGRectGetMidY( rect ), maxy = CGRectGetMaxY( rect );

    CGContextBeginPath( context );
    CGContextSetGrayFillColor( context, 1.0, 0.0 );
    CGContextAddRect( context, rect );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    CGContextSetGrayFillColor( context, 1.0, 1.0 );
    CGContextBeginPath( context );
    CGContextMoveToPoint( context, minx, midy );
    CGContextAddArcToPoint( context, minx, miny, midx, miny, radius_bl );
    CGContextAddArcToPoint( context, maxx, miny, maxx, midy, radius_br );
    CGContextAddArcToPoint( context, maxx, maxy, midx, maxy, radius_tr );
    CGContextAddArcToPoint( context, minx, maxy, minx, midy, radius_tl );
    CGContextClosePath( context );
    CGContextDrawPath( context, kCGPathFill );

    // Create CGImageRef of the main view bitmap content, and then
    // release that bitmap context
    CGImageRef bitmapContext = CGBitmapContextCreateImage( context );
    CGContextRelease( context );

    // convert the finished resized image to a UIImage 
    UIImage *theImage = [UIImage imageWithCGImage:bitmapContext];
    // image is retained by the property setting above, so we can 
    // release the original
    CGImageRelease(bitmapContext);

    // return the image
    return theImage;
}  

Teraz potrzebujesz tylko kilku wierszy kodu. I umieścić rzeczy w moim viewController viewDidLoadmetody, ponieważ jest to szybsze, ale można go używać również w zwyczaju UIViewz layoutSubviewsmetody opisanej w przykładzie.



- (void)viewDidLoad {

    // Create the mask image you need calling the previous function
    UIImage *mask = MTDContextCreateRoundedMask( self.view.bounds, 50.0, 50.0, 0.0, 0.0 );
    // Create a new layer that will work as a mask
    CALayer *layerMask = [CALayer layer];
    layerMask.frame = self.view.bounds;       
    // Put the mask image as content of the layer
    layerMask.contents = (id)mask.CGImage;       
    // set the mask layer as mask of the view layer
    self.view.layer.mask = layerMask;              

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

Rozwiązanie 2

To rozwiązanie jest nieco bardziej „brudne”. Zasadniczo możesz utworzyć warstwę maski z zaokrąglonym rogiem, którego potrzebujesz (wszystkie rogi). Następnie należy zwiększyć wysokość warstwy maski o wartość promienia narożnika. W ten sposób dolne zaokrąglone rogi są ukryte i widać tylko górny zaokrąglony róg. Umieściłem kod tylko w viewDidLoadmetodzie, ponieważ jest szybszy, ale możesz go użyć również w swoim niestandardowym UIViewz layoutSubviewsmetodą z przykładu.

  

- (void)viewDidLoad {

    // set the radius
    CGFloat radius = 50.0;
    // set the mask frame, and increase the height by the 
    // corner radius to hide bottom corners
    CGRect maskFrame = self.view.bounds;
    maskFrame.size.height += radius;
    // create the mask layer
    CALayer *maskLayer = [CALayer layer];
    maskLayer.cornerRadius = radius;
    maskLayer.backgroundColor = [UIColor blackColor].CGColor;
    maskLayer.frame = maskFrame;

    // set the mask
    self.view.layer.mask = maskLayer;

    // Add a backaground color just to check if it works
    self.view.backgroundColor = [UIColor redColor];
    // Add a test view to verify the correct mask clipping
    UIView *testView = [[UIView alloc] initWithFrame:CGRectMake( 0.0, 0.0, 50.0, 50.0 )];
    testView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:testView];
    [testView release];

    [super viewDidLoad];
}

Mam nadzieję że to pomoże. Cześć!

lomanf
źródło
Wreszcie zadziałało! Dzięki wielkie! Jaka jest przewaga jednego nad drugim?
Jumhyn,
W każdym razie nawet rozwiązanie CAShapeLayer jest idealne. Można narysować CGPath pomocą UIBezierPath (który potrzebuje 3.2 lub nowszym) lub tylko z metodami CGPath (CGPathMoveToPoint, CGPathAddQuadCurveToPoint itd.)
lomanf
1
Jak ustawić automatyczną zmianę rozmiaru maski po obróceniu z pionowej na poziomą?
chourobin
Nie zapomnij dodać layer.masksToBounds = YES
Dmitry Salnikov
jak to zrobić, aby zaokrąglić tylko prawy górny i dolny róg?
PLOW
69

Czesanie przez kilka odpowiedzi i komentarze, I okazało się, że za pomocą UIBezierPath bezierPathWithRoundedRecti CAShapeLayernajprostszym i najbardziej prosty sposób. To może nie być odpowiednie dla bardzo skomplikowanych przypadków, ale w przypadku sporadycznego zaokrąglania rogów działa szybko i płynnie.

Stworzyłem uproszczonego pomocnika, który ustawia odpowiedni róg w masce:

-(void) setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
    UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(10.0, 10.0)];

    CAShapeLayer* shape = [[CAShapeLayer alloc] init];
    [shape setPath:rounded.CGPath];

    view.layer.mask = shape;
}

Aby z niego skorzystać, wystarczy zadzwonić z odpowiednim wyliczeniem UIRectCorner, np .:

[self setMaskTo:self.photoView byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft];

Zwróć uwagę, że dla mnie używam go do zaokrąglania rogów zdjęć w zgrupowanym UITableViewCell, promień 10.0 działa dobrze, jeśli trzeba tylko odpowiednio zmienić wartość.

EDYTUJ: po prostu zwróć uwagę na poprzednią odpowiedź bardzo podobną do tej ( link ). W razie potrzeby możesz nadal używać tej odpowiedzi jako dodatkowej funkcji zapewniającej wygodę.


EDYCJA: ten sam kod co rozszerzenie UIView w Swift 3

extension UIView {
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

Aby z niego skorzystać, wystarczy zadzwonić maskByRoundingCornerdo dowolnego UIView:

view.maskByRoundingCorners([.topLeft, .bottomLeft])
PL
źródło
Czy jest to możliwe, jeśli nadal dodajesz obramowanie?
Unome
2
Musiałem umieścić wywołanie funkcji w DispatchQueue.main.async {}, ponieważ tylko jeden róg został zaokrąglony ...
Maksim Kniazev
1
@MaksimKniazev tak, to musi być wywołane z głównego wątku (UI). Ta zasada dotyczy wszelkich akcji dotykających interfejsu użytkownika, muszą być wywoływane z głównego wątku lub mogą się wydarzyć dziwne rzeczy.
PL
1
tylko jeden róg wydaje się zaokrąglać. wypróbowane rozwiązanie @MaksimKniazev też nie działało.
Gustavo_fringe
Mam ten sam problem co @Gustavo_fringe, gdzie prawy górny róg nie jest przycięty / zamaskowany do granic wewnątrz (zmieniony rozmiar) widoku xib, nie wiem jeszcze, jak to naprawić.
CyberMew
57

Nie mogłem tego wszystkiego zmieścić w komentarzu do odpowiedzi @ lomanf. Więc dodaję to jako odpowiedź.

Jak powiedział @lomanf, musisz dodać maskę warstwy, aby zapobiec rysowaniu podwarstw poza granicami ścieżki. Jednak teraz jest to o wiele łatwiejsze. Jeśli celujesz w system iOS 3.2 lub nowszy, nie musisz tworzyć obrazu z kwarcem i ustawiać go jako maski. Możesz po prostu utworzyć CAShapeLayerz a UIBezierPathi użyć tego jako maski.

Jeśli używasz masek warstw, po dodaniu maski upewnij się, że maskowana warstwa nie jest częścią żadnej hierarchii warstw. W przeciwnym razie zachowanie jest niezdefiniowane. Jeśli Twój widok jest już w hierarchii, musisz usunąć go z jego nadzoru, zamaskować, a następnie umieścić z powrotem na miejscu.

CAShapeLayer *maskLayer = [CAShapeLayer layer];
UIBezierPath *roundedPath = 
  [UIBezierPath bezierPathWithRoundedRect:maskLayer.bounds
                        byRoundingCorners:UIRectCornerTopLeft |
                                          UIRectCornerBottomRight
                              cornerRadii:CGSizeMake(16.f, 16.f)];    
maskLayer.fillColor = [[UIColor whiteColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];

//Don't add masks to layers already in the hierarchy!
UIView *superview = [self.view superview];
[self.view removeFromSuperview];
self.view.layer.mask = maskLayer;
[superview addSubview:self.view];

Ze względu na sposób działania renderowania Core Animation maskowanie jest operacją stosunkowo powolną. Każda maska ​​wymaga dodatkowego przejścia renderowania. Dlatego oszczędnie używaj masek.

Jedną z najlepszych części tego podejścia jest to, że nie musisz już tworzyć niestandardowego UIViewi zastępowania drawRect:. To powinno uczynić twój kod prostszym, a może nawet szybszym.

Nathan Eror
źródło
Wydaje się obiecujące, ale widok się nie pojawia. Jakieś pomysły, dlaczego?
Jumhyn
10
Używasz maskLayer.bounds do zmiany rozmiaru UIBezierPath, ale wartość bounds to CGRectZero. Musisz dodać drugą linię kodu (natychmiast po inicjalizacji CAShapeLayer), na przykład maskLayer.frame = self.view.bounds;
lomanf
1
Tak, masz rację. Wyodrębniłem ten kod z innego przykładu, w którym warstwa kształtu już istniała. Przepraszam za to.
Nathan Eror,
Działa idealnie! Dodanie maskLayer.frame = self.view.bounds, jak wspomniano powyżej, działa. W przeciwnym razie widoki się nie wyświetlą.
Daniel Ribeiro,
+1 Działa to całkiem dobrze. Właściwie myślę, że wolę tę metodę od rozwiązania nr 2 Lomanfa. Ta metoda z łatwością pozwala na dodawanie łuków do przeciwległych rogów (np .: Prawy górny + Lewy dolny) i jest z pewnością krótsza niż pierwsze rozwiązanie Lomanfa. Świetny kod Nathan!
FreeAsInBeer
30

Wziąłem przykład Nathana i utworzyłem kategorię, UIViewaby umożliwić przestrzeganie zasad DRY. Bez zbędnych ceregieli:

UIView + Roundify.h

#import <UIKit/UIKit.h>

@interface UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;
-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii;

@end

UIView + Roundify. M

#import "UIView+Roundify.h"

@implementation UIView (Roundify)

-(void)addRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CALayer *tMaskLayer = [self maskForRoundedCorners:corners withRadii:radii];
    self.layer.mask = tMaskLayer;
}

-(CALayer*)maskForRoundedCorners:(UIRectCorner)corners withRadii:(CGSize)radii {
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.frame = self.bounds;

    UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:
        maskLayer.bounds byRoundingCorners:corners cornerRadii:radii];
    maskLayer.fillColor = [[UIColor whiteColor] CGColor];
    maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
    maskLayer.path = [roundedPath CGPath];

    return maskLayer;
}

@end

Zadzwonić:

[myView addRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
                withRadii:CGSizeMake(20.0f, 20.0f)];
FreeAsInBeer
źródło
2
Nie zapomnij zaimportować <QuartzCore / QuatzCore.h> do pliku .m, a także dodać QuartzCore.framework do projektu
DEzra
+1 Świetna rzecz, ale jedno pytanie, jaki jest cel usuwania / odczytywania widoku do jego superviewu?
eladleb
@eladleb: Podstawowy kod pochodzi od Nathana, po prostu go trochę zoptymalizowałem. Na podstawie przeprowadzonych przeze mnie badań nie mogę znaleźć uzasadnionego powodu, aby usunąć widok przed nałożeniem maski warstwy. Dlatego uważam, że bezpiecznie jest pominąć ten kod.
FreeAsInBeer
@FreeAsInBeer - Zgadzam się, to będę głosować na usunięcie go, to rzeczywiście zmienia widok z-porządek i może stworzyć więcej problemów ..
eladleb
Rozwiązano problem, który miałem z dodawaniem zaokrąglonych rogów do TableView: dodałem nagłówek, zaokrąglone górne rogi i dodałem stopkę z zaokrąglonymi dolnymi rogami. Dzięki.
naprawiono noc
24

Aby rozszerzyć nieco odpowiedź PL, przepisałem metodę tak, aby nie zaokrąglała pewnych obiektów, takich jak UIButtonpoprawnie

- (void)setMaskTo:(id)sender byRoundingCorners:(UIRectCorner)corners withCornerRadii:(CGSize)radii
{
    // UIButton requires this
    [sender layer].cornerRadius = 0.0;

    UIBezierPath *shapePath = [UIBezierPath bezierPathWithRoundedRect:[sender bounds]
                                                byRoundingCorners:corners
                                                cornerRadii:radii];

    CAShapeLayer *newCornerLayer = [CAShapeLayer layer];
    newCornerLayer.frame = [sender bounds];
    newCornerLayer.path = shapePath.CGPath;
    [sender layer].mask = newCornerLayer;
}

I zadzwoń do tego

[self setMaskTo:self.continueButton byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight withCornerRadii:CGSizeMake(3.0, 3.0)];
Josh Valdivieso
źródło
12

Jeśli chcesz to zrobić w Swift, możesz użyć rozszerzenia UIView. W ten sposób wszystkie podklasy będą mogły używać następującej metody:

import QuartzCore

extension UIView {
    func roundCorner(corners: UIRectCorner, radius: CGFloat) {
        let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = maskPath.CGPath
        layer.mask = maskLayer
    }
}    

Przykładowe użycie:

self.anImageView.roundCorner(.topRight, radius: 10)
Kevin Delord
źródło
3

Rozszerzając zaakceptowaną odpowiedź, dodajmy do niej kompatybilność wsteczną. Przed iOS 11, view.layer.maskedCorners nie jest dostępny. Więc możemy to zrobić

if #available(iOS 11.0, *) {
    myView.layer.maskedCorners = [.layerMinXMinYCorner,.layerMaxXMinYCorner]
} else {
    myView.maskByRoundingCorners([.topLeft, .topRight])
}

extension UIView{
    func maskByRoundingCorners(_ masks:UIRectCorner, withRadii radii:CGSize = CGSize(width: 10, height: 10)) {
        let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: masks, cornerRadii: radii)

        let shape = CAShapeLayer()
        shape.path = rounded.cgPath

        self.layer.mask = shape
    }
}

Napisaliśmy maskByRoundingCorners jako rozszerzenie UIView, aby usprawniało ponowne wykorzystanie kodu.

Kredyty dla @SachinVsSachin i @PL :) Połączyłem ich kody, aby było lepiej.

Rizwan Ahmed
źródło
2
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(5, 5, self.bounds.size.width-10, self.bounds.size.height-10)
                                               byRoundingCorners:UIRectCornerAllCorners
                                                     cornerRadii:CGSizeMake(12.0, 12.0)];

zmień „AllCorners” zgodnie z potrzebami.

Umang
źródło
więc czy musisz to zrobić w UIView, czy można to zrobić w UIViewController? Co robisz z UIBezierPath, gdy już to zrobisz? Dzięki.
StoneBreaker
2

Wszystkie dostarczone rozwiązania osiągają cel. Ale UIConstraintsczasami może to wysadzić.

Na przykład dolne rogi muszą być zaokrąglone. Jeśli ograniczenie wysokości lub dolnych odstępów jest ustawione na UIView, które należy zaokrąglić, fragmenty kodu, które zaokrągla rogi, należy przenieść do viewDidLayoutSubviewsmetody.

Podświetlanie:

UIBezierPath *maskPath = [UIBezierPath
    bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:
    (UIRectCornerTopRight | UIRectCornerBottomRight) cornerRadii:CGSizeMake(16.0, 16.0)];

Powyższy fragment kodu zaokrągla prawy górny róg tylko wtedy, gdy ten kod jest ustawiony w viewDidLoad. Ponieważ roundedView.boundszmieni się po zaktualizowaniu wiązań UIView.

hasan
źródło
1

Utwórz maskę i ustaw ją na warstwie widoku

Steve Riggins
źródło
Jak bym to zrobił? Jestem trochę niepewny, jeśli chodzi o zarządzanie widokiem niższego poziomu.
Jumhyn
1
stackoverflow.com/questions/2264083/… może ci pomóc
Steve Riggins
1
Cieszę się, że moja odpowiedź wciąż pomaga ludziom!
nevan king
1

Zaczynając od kodu, możesz użyć czegoś takiego jak poniższy fragment.

Nie jestem pewien, czy o tego rodzaju wynikach chodzi. Warto również zauważyć, że jeśli / kiedy system wywoła drawRect: ponownie, prosząc tylko o przerysowanie tylko części rekta, zachowa się to bardzo dziwnie. Podejście Nevana , opisane powyżej, może być lepszym sposobem.

 // make sure the view's background is set to [UIColor clearColor]
- (void)drawRect:(CGRect)rect
{
    CGFloat radius = 10.0;
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, rect.size.width/2, rect.size.height/2);
    CGContextRotateCTM(context, M_PI); // rotate so image appears right way up
    CGContextTranslateCTM(context, -rect.size.width/2, -rect.size.height/2);

    CGContextBeginPath(context);

    CGContextMoveToPoint(context, rect.origin.x, rect.origin.y);
    CGContextAddArc(context, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1);
    CGContextAddArc(context, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1);
    CGContextAddLineToPoint(context, rect.origin.x + rect.size.width, rect.origin.y);
    CGContextClip(context); 

    // now do your drawing, e.g. draw an image
    CGImageRef anImage = [[UIImage imageNamed:@"image.jpg"] CGImage];
    CGContextDrawImage(context, rect, anImage);    
}
Ukośnie
źródło
Opracowałem widok z zaokrąglonymi narożnikami, ale nie mogę sprawić, by przycinał którykolwiek z jego podglądów podrzędnych. Jeśli umieszczę widok z kwadratowymi narożnikami na skraju mojego widoku, nadal zakrywa on zaokrąglone rogi swojego nadzoru. Jakiś pomysł dlaczego tak się dzieje?
Jumhyn,
Ponieważ przycinanie -drawRect:wpływa tylko na widok, który rysujesz. Jego podglądy są rysowane niezależnie po zakończeniu rysowania.
Jonathan Grynspan,
1

Nieco hackerskim, ale stosunkowo prostym (bez podklas, maskowania itp.) Sposobem jest utworzenie dwóch UIViews. Obie z clipToBounds = YES. Ustaw zaokrąglone rogi w widoku podrzędnym, a następnie umieść go w widoku nadrzędnym, tak aby rogi, które chcesz, aby były proste, zostały przycięte.

UIView* parent = [[UIView alloc] initWithFrame:CGRectMake(10,10,100,100)];
parent.clipsToBounds = YES;

UIView* child = [[UIView alloc] new];
child.clipsToBounds = YES;
child.layer.cornerRadius = 3.0f;
child.backgroundColor = [UIColor redColor];
child.frame = CGRectOffset(parent.bounds, +4, -4);


[parent addSubView:child];

Nie obsługuje przypadku, w którym chcesz zaokrąglić dwa przeciwległe rogi po przekątnej.

William Denniss
źródło
1

Ścieżka Beziera jest odpowiedzią, jeśli potrzebujesz dodatkowego kodu, ten działał dla mnie: https://stackoverflow.com/a/13163693/936957

UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:_backgroundImageView.bounds 
                                 byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                                       cornerRadii:CGSizeMake(3.0, 3.0)];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
_backgroundImageView.layer.mask = maskLayer;
[maskLayer release];
Yunus Nedim Mehel
źródło
1

Rozwiązanie UIBezierPath.

- (void) drawRect:(CGRect)rect {

    [super drawRect:rect];

    //Create shape which we will draw. 
    CGRect rectangle = CGRectMake(2, 
                                  2, 
                                  rect.size.width - 4,
                                  rect.size.height - 4);

    //Create BezierPath with round corners
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rectangle 
                                                   byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight 
                                                         cornerRadii:CGSizeMake(10.0, 10.0)];

    //Set path width
    [maskPath setLineWidth:2];

    //Set color
    [[UIColor redColor] setStroke];

    //Draw BezierPath to see it
    [maskPath stroke];
}
Alexey Bondarchuk
źródło
0

Może to działać tylko wtedy, gdy niektóre rzeczy są ustawione poprawnie:

  1. clipsToBounds muszą być ustawione na YES
  2. nieprzezroczysty musi być NIE
  3. backgroundColor powinno mieć wartość „clearColor” (nie jestem tego do końca pewien)
  4. contentMode musi mieć wartość „UIContentModeRedraw”, ponieważ funkcja drawRect nie jest wywoływana często, jeśli nie jest
  5. [super drawRect: rect] musi być wywołane po CGContextClip
  6. Twój widok nie może zawierać dowolnych podglądów podrzędnych (nie jestem pewien)
  7. Pamiętaj, aby przynajmniej raz ustawić „needDisplay:”, aby wywołać drawrect
Dunkelstern
źródło
0

Zdaję sobie sprawę, że próbujesz zaokrąglić dwa górne rogi UITableView, ale z jakiegoś powodu stwierdziłem, że najlepszym rozwiązaniem jest użycie:

self.tableView.layer.cornerRadius = 10;

Programowo powinien zaokrąglać wszystkie cztery rogi, ale z jakiegoś powodu zaokrągla tylko dwa górne rogi. ** Proszę zobaczyć zrzut ekranu poniżej, aby zobaczyć efekt kodu, który napisałem powyżej.

Mam nadzieję, że to pomoże!

wprowadź opis obrazu tutaj

Adam Cooper
źródło
1
Jeśli nie zaokrągla wszystkich rogów, UITableViewmusi istnieć kod w innym miejscu, który powoduje, że UITableViewnie ma zaokrąglonych rogów na dole, lub może nawet a UIViewlub, UILabelktóry zakrywa dolną część UITableView.
Josh Valdivieso
-1

Prawdopodobnie musisz przyciąć do granic. Dodaj linię

self.clipsToBounds = YES

gdzieś w kodzie, aby ustawić tę właściwość.

Ned
źródło