2009-11-11 15 views
26

Tworzę niestandardowy UISlider, aby przetestować niektóre pomysły interfejsu. Opiera się głównie na powiększaniu obrazu kciuka.Jak uzyskać środek obrazu kciuka UISlider

I okazało się, jak to zrobić, tak:

UIImage *thumb = [UIImage imageNamed:@"newThumbImage_64px.png"]; 
[self.slider setThumbImage:thumb forState:UIControlStateNormal]; 
[self.slider setThumbImage:thumb forState:UIControlStateHighlighted]; 
[thumb release]; 

Aby obliczyć powiązany wartość muszę wiedzieć, gdzie punkt centrum obrazu kciuka spada kiedy jest manipulowany. A punkt powinien znajdować się w jego współrzędnych superview.

Patrząc na dokumenty UISlider, nie widziałem żadnej właściwości, która to śledziłaby.

Czy istnieje prosty sposób obliczenia tego lub czy może on pochodzić z niektórych istniejących wartości?

Odpowiedz

2

po trochę grania z IB i 1px kciukiem szerokiego obrazu, pozycja kciuka jest dokładnie tam, gdzie chcesz go spodziewać:

UIImage  *thumb = [UIImage imageNamed:@"newThumbImage_64px.png"]; 
CGRect  sliderFrame = self.slider.frame; 
CGFloat  x = sliderFrame.origin.x + slideFrame.size.width * slider.value + thumb.size.width/2; 
CGFloat  y = sliderFrame.origin.y + sliderFrame.size.height/2; 

return CGPointMake(x, y); 
+0

Zakładając zakres suwaka 0..1, punkt jest poprawna, gdy slider.value wynosi 0, ale staje się coraz bardziej off jako slider.value zmian. W równaniu x jest też literówka. – willc2

+0

Co więcej, czy suwakFrame.x nie powinien mieć wartości sliderFrame.origin.x i sliderFrame.size.width? – willc2

+0

Ups, przepraszam za literówkę, poprawiono. –

53

ten powróci prawidłowe położenie X środka kciuka obrazu z UISlider mając współrzędne:

- (float)xPositionFromSliderValue:(UISlider *)aSlider { 
    float sliderRange = aSlider.frame.size.width - aSlider.currentThumbImage.size.width; 
    float sliderOrigin = aSlider.frame.origin.x + (aSlider.currentThumbImage.size.width/2.0); 

    float sliderValueToPixels = (((aSlider.value - aSlider.minimumValue)/(aSlider.maximumValue - aSlider.minimumValue)) * sliderRange) + sliderOrigin; 

    return sliderValueToPixels; 
} 

Umieścić go w kontroler widoku i używać go tak: (zakłada właściwość o nazwie suwak)

float x = [self xPositionFromSliderValue:self.slider]; 
+3

to działa dla mnie, z wyjątkiem konieczności zmiany, aby umożliwić suwaki o niestandardowych wartościach min/max: float sliderValueToPixels = (aSlider.value/aSlider.maximumValue * sliderRange) + suwakOrigin; Nazywa się tak: float x = [self xPositionFromSliderValue: someSlider]; –

+0

Doskonały w połączeniu z modyfikacją Jamesa –

+7

Właściwie w celu dostosowania niestandardowej wartości minimalnej, która nie jest równa 0 (lub blisko niej), należy ją zmienić na: float sliderValueToPixels = ((((a (Wartość domyślna-aSlider.minimumValue))/(aSlider.maximumValue-aSlider.minimumValue)) * sliderRange) + sliderOrigin; – Joel

6

Podszedłem do tego, najpierw odwzorowując interwał wartości UISlider w procentach, a następnie biorąc ten sam procent wielkości suwaka pomniejszony o procent rozmiaru kciuka, wartość, do której dodałem połowę rozmiaru kciuka, aby uzyskać jego środek.

- (float)mapValueInIntervalInPercents: (float)value min: (float)minimum max: (float)maximum 
    { 
     return (100/(maximum - minimum)) * value - 
       (100 * minimum)/(maximum - minimum); 
    }  


    - (float)xPositionFromSliderValue:(UISlider *)aSlider 
    { 
     float percent = [self mapValueInIntervalInPercents: aSlider.value 
           min: aSlider.minimumValue 
           max: aSlider.maximumValue]/100.0; 

     return percent * aSlider.frame.size.width - 
       percent * aSlider.currentThumbImage.size.width + 
       aSlider.currentThumbImage.size.width/2; 
    } 
12

Chciałbym wiedzieć, dlaczego nikt z was nie podał najprostszej odpowiedzi, która polega na przeczytaniu instrukcji. Można obliczyć wszystkie te wartości, a także dokładnie upewniając się, że pobyt w ten sposób, po prostu za pomocą metod:

- (CGRect)trackRectForBounds:(CGRect)bounds 
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value 

które można łatwo znaleźć w dokumentacji dla programistów.

Jeśli zmienia się obraz kciuka i chcesz zmienić jego położenie, podklasę i nadpisanie tych metod. Pierwszy daje prostokąt, w którym kciuk może przesunąć drugie położenie kciuka.

+1

To jest ostateczna odpowiedź! Po prostu zaktualizuj zmienną składową zawierającą zwracaną wartość [super trackRectForBounds] i możesz pominąć wszystkie obliczenia. Doskonały! – Krumelur

+6

Dokumentacja mówi dla obu tych metod: "Nie powinieneś bezpośrednio wywoływać tej metody." –

+1

@IgorKulagin Nie musisz dzwonić bezpośrednio ... – Beppe

18

Próbowałem ten po przeczytaniu powyższej sugestii -

yourLabel = [[UILabel alloc]initWithFrame:....]; 

//Call this method on Slider value change event 

-(void)sliderValueChanged{ 
    CGRect trackRect = [self.slider trackRectForBounds:self.slider.bounds]; 
    CGRect thumbRect = [self.slider thumbRectForBounds:self.slider.bounds 
           trackRect:trackRect 
            value:self.slider.value]; 

    yourLabel.center = CGPointMake(thumbRect.origin.x + self.slider.frame.origin.x, self.slider.frame.origin.y - 20); 
} 

mogę uzyskać najdokładniejsze wartości za pomocą tego fragmentu.

0

Powyższe rozwiązanie jest przydatne, gdy UISlider jest poziomy. W ostatnim projekcie musimy użyć UISlider z kątem. Muszę więc uzyskać zarówno pozycję x, jak i y. Użyj poniższego, aby obliczyć oś x, y:

- (CGPoint)xyPositionFromSliderValue:(UISlider *)aSlider WithAngle:(double)aangle{ 
    //aangle means the dextrorotation angle compare to horizontal. 
    float xOrigin = 0.0; 
    float yOrigin = 0.0; 
    float xValueToaXis=0.0; 
    float yValueToaXis=0.0; 
    float sliderRange = slider_width-aSlider.currentThumbImage.size.width; 
    xOrigin = aSlider.frame.origin.x+slider_width*fabs(cos(aangle/180.0*M_PI)); 
    yOrigin = aSlider.frame.origin.y; 

    xValueToaXis = xOrigin + ((((((aSlider.value-aSlider.minimumValue)/(aSlider.maximumValue-aSlider.minimumValue)) * sliderRange))+(aSlider.currentThumbImage.size.width/2.0))*cos(aangle/180.0*M_PI)) ; 
    yValueToaXis = yOrigin + ((((((aSlider.value-aSlider.minimumValue)/(aSlider.maximumValue-aSlider.minimumValue)) * sliderRange))+(aSlider.currentThumbImage.size.width/2.0))*sin(aangle/180.0*M_PI)); 

    CGPoint xyPoint=CGPointMake(xValueToaXis, yValueToaXis); 
    return xyPoint; 
} 

Poza tym, czy mogę utworzyć suwak Ranger oparty na UISlider? Dzięki.

10

Zamiast tego lepiej użyć metody -[UIView convertRect:fromView:]. To czystsze i łatwiej bez żadnych skomplikowanych obliczeń:

- (IBAction)scrub:(UISlider *)sender 
{ 
    CGRect _thumbRect = [sender thumbRectForBounds:sender.bounds 
             trackRect:[sender trackRectForBounds:sender.bounds] 
              value:sender.value]; 
    CGRect thumbRect = [self.view convertRect:_thumbRect fromView:sender]; 

    // Use the rect to display a popover (pre iOS 8 code) 
    [self.popover dismissPopoverAnimated:NO]; 
    self.popover = [[UIPopoverController alloc] initWithContentViewController:[UIViewController new]]; 
    [self.popover presentPopoverFromRect:thumbRect inView:self.view 
       permittedArrowDirections:UIPopoverArrowDirectionDown|UIPopoverArrowDirectionUp animated:YES]; 
} 
+2

To jest odpowiedź ostateczna. Nie ma potrzeby stosowania podklasy UISlider. Dzięki Tobie – klefevre

+0

tak, to jest właściwa odpowiedź. –

1

Oto Swift 2.2 rozwiązanie, stworzyłem rozszerzenie dla niego. Próbowałem tylko tego z domyślnym obrazem.

import UIKit 

extension UISlider { 

    var thumbImageCenterX: CGFloat { 
     let trackRect = trackRectForBounds(bounds) 
     let thumbRect = thumbRectForBounds(bounds, trackRect: trackRect, value: value) 

     return thumbRect.origin.x + thumbRect.width/2 - frame.size.width/2 
    } 
} 
+0

Dziękujemy! :) !! –

11

Swift 3

extension UISlider { 
    var thumbCenterX: CGFloat { 
     let trackRect = self.trackRect(forBounds: frame) 
     let thumbRect = self.thumbRect(forBounds: bounds, trackRect: trackRect, value: value) 
     return thumbRect.midX 
    } 
} 
0

To będzie pracować dla UISlider być umieszczony w dowolnym miejscu na ekranie. Większość innych rozwiązań będzie działać tylko wtedy, gdy UISlider zostanie wyrównany z lewą krawędzią ekranu. Uwaga, użyłem frame zamiast bounds dla thumbRect, aby to osiągnąć. I pokażę dwie odmiany, opartych na wykorzystaniu frame lub bounds dla trackRect

extension UISlider { 

    //this version will return the x coordinate in relation to the UISlider frame 
    var thumbCenterX: CGFloat { 
     return thumbRect(forBounds: frame, trackRect: trackRect(forBounds: bounds), value: value).midX 
    } 

    //this version will return the x coordinate in relation to the UISlider's containing view 
    var thumbCenterX: CGFloat { 
     return thumbRect(forBounds: frame, trackRect: trackRect(forBounds: frame), value: value).midX 
    } 

} 
1

Swift 3.0
Proszę zapoznać, jeśli chcesz.

import UIKit 

extension UISlider { 

    var trackBounds: CGRect { 
     return trackRect(forBounds: bounds) 
    } 

    var trackFrame: CGRect { 
     guard let superView = superview else { return CGRect.zero } 
     return self.convert(trackBounds, to: superView) 
    } 

    var thumbBounds: CGRect { 
     return thumbRect(forBounds: frame, trackRect: trackBounds, value: value) 
    } 

    var thumbFrame: CGRect { 
     return thumbRect(forBounds: bounds, trackRect: trackFrame, value: value) 
    } 
}