.m:
-(void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
if(CGRectIntersectsRect(ball.bounds , fang.bounds))
{
UIImage *image = [UIImage imageNamed:#"guy.png"];
UIImageView *imageview = [[UIImageView alloc] initWithImage:image];
[self.view addSubView:imageview];
[imageview release];
}
NSLog(#"x : %g", acceleration.x);
NSLog(#"y : %g", acceleration.y);
NSLog(#"z : %g", acceleration.z);
delta.y = acceleration.y * 60;
delta.x = acceleration.x * 60;
ball.center = CGPointMake(ball.center.x + delta.x, ball.center.y + delta.y);
// Right
if(ball.center.x < 0) {
ball.center = CGPointMake(320, ball.center.y);
}
// Left
if(ball.center.x > 320) {
ball.center = CGPointMake(0, ball.center.y);
}
// Top
if(ball.center.y < 0) {
ball.center = CGPointMake(ball.center.x, 460);
}
// Bottom
if(ball.center.y > 460) {
ball.center = CGPointMake(ball.center.x, 0);
}
}
.h:
UIImageView *ball;
IBOutlet UIImageView *fang;
MY TROUBLES:
When I open the app, the image #"guy.png" reveals itself without anything touching the fang. I need help. ALSO//""**My accelerometer is SO choppy and barely works with this .m code :
if(CGRectIntersectsRect(ball.bounds , fang.bounds))
{
UIImage *image = [UIImage imageNamed:#"endScreenImage.png"];
UIImageView *imageview = [[UIImageView alloc] initWithImage:image];
[self.view addSubView:imageview];
[imageview release];
}
HELP PLEASE
A few issues:
CGRectIntersectsRect() is returning YES because you're comparing the bounds of your objects, not the frames. The bounds of an object is the rectangle describing how the object sees its own dimensions. It generally has its origin at (0,0), and the size is the size of the view. Since both views have a bounds starting at (0,0), the bounds rectangles do intersect.
What you really want is the frame of the views. The frame of a view represents the space occupied in that view, with respect to its superview. That is:
if (CGRectIntersectsRect(ball.frame, fang.frame)) { // etc. }
The frame represents the location of the view. The bounds represents the location of the content within the view itself. When you're comparing the position of two views, you almost always want to use frame.
As currently written, your code will add a new subview every time there's an intersection, but will never remove the old subview. Since the accelerometer can fire multiple times every second, this can result in a lot of views being added to the view hierarchy. That could significantly impact performance, and may be why you're seeing such poor performance. I would recommend instead creating a single UIImageView as an instance variable, and then use the hidden property to control whether it's visible or not.
Thus, I'd recommend you modify your code as follows:
if (CGRectIntersectsRect(ball.frame, fang.frame)) {
guyView.hidden = NO;
}
else {
guyView.hidden = YES;
}
Related
I have a view, embedded into a container that it itself inside a UIScrollView.
This view contains a text aligned to its edges as well as a button, again aligned to edges.
When launching the controller view, inside viewDidLayoutSubviews, I get the actual real height of the screen, then I call the view to be resized this way :
- (void)viewDidLayoutSubviews {
[self log:#"ViewDidLayoutSubviews"];
//::.. change scroll height according to main view ..::
float mainHeight = _subMainView.frame.size.height;
float scrollHeight = mainHeight - _scrollView.frame.origin.y;
_scrollView.frame = CGRectMake(_scrollView.frame.origin.x, _scrollView.frame.origin.y, _scrollView.frame.size.width, scrollHeight);
//::.. find buttons in child ..::
UIViewController *child = [self.childViewControllers lastObject];
//::.. Get only block views ::..
NSArray *blockViews = [myTools getAllSubviewsFromView:child.view ofClass:[UIView class] byTagFrom:1000 toTag:1010];
[self resizeViews:blockViews intoFrame:_scrollView.frame withVerticalPadding:16.0f andTopMargin:(31.0f+16.0f) andBottomMargin:0.0f andLeftMargin:16.0f andRightMargin:16.0f];
NSArray *buttons = [myTools getAllSubviewsFromView:child.view ofClass:[UIButton class]];
[self resizeViews:buttons intoFrame:_scrollView.frame withVerticalPadding:16.0f andTopMargin:(31.0f+16.0f) andBottomMargin:0.0f andLeftMargin:16.0f andRightMargin:16.0f];
for (UIButton *button in buttons) {
[button invalidateIntrinsicContentSize];
}
}
And here is the resize code in question :
+ (void)resizeViews:(NSArray *)views intoFrame:(CGRect)parentFrame withVerticalPadding:(float)verticalPadding andTopMargin:(float)topMargin andBottomMargin:(float)bottomMargin andLeftMargin:(float)leftMargin andRightMargin:(float)rightMargin {
float blockHeight = ((parentFrame.size.height - topMargin - bottomMargin) / views.count);
blockHeight -= verticalPadding;
float origin = topMargin;
for (UIView *aView in views) {
[aView layoutIfNeeded];
for (NSLayoutConstraint *constraint in aView.constraints) {
if (constraint.firstAttribute == NSLayoutAttributeHeight) {
constraint.constant = blockHeight;
break;
}
}
origin += blockHeight + verticalPadding;
[aView setNeedsUpdateConstraints];
[aView layoutIfNeeded];
}
}
The visual result is good because both views and there content (the buttons) are correctly resized (the button is aligned centered vertically and horizontally so it is easy to show the good alignment).
However, the first button has is whole area touchable where the second one, under it, can be touched only on its upper height, before its middle...
Can't understand how it is possible that a button, who's size is good, become untouchable on its whole part ?
Any help ? :)
Thanks a lot.
I suppose it's not strictly in line with Apple guidelines but I guess it must be possible somehow. I'd like to change the height of navigation bar inside UINavigationController and the height of UIBarButtonItem elements inside that bar.
Using a trick from this question I managed to change the height of navigation bar but I can see no way of adjusting the height of bar button items.
If anyone knows how to change the size of bar button items, please help me out.
This is my solution. It works very well.
#interface UINavigationBar (CustomHeight)
#end
#implementation UINavigationBar (CustomHeight)
- (CGSize)sizeThatFits:(CGSize)size {
// Change navigation bar height. The height must be even, otherwise there will be a white line above the navigation bar.
CGSize newSize = CGSizeMake(self.frame.size.width, 40);
return newSize;
}
-(void)layoutSubviews {
[super layoutSubviews];
// Make items on navigation bar vertically centered.
int i = 0;
for (UIView *view in self.subviews) {
NSLog(#"%i. %#", i, [view description]);
i++;
if (i == 0)
continue;
float centerY = self.bounds.size.height / 2.0f;
CGPoint center = view.center;
center.y = centerY;
view.center = center;
}
}
Maybe this tutorial about a customized navbar will help you: Recreating the iBooks wood themed navigation bar
If you create a BarButtonItem with a UIImageView you can maybe change the framesize/boundsize of the custom UIImageView
UIImageView* imageView = [[[UIImageView alloc] initWithFrame:navigationController.navigationBar.frame] autorelease];
imageView.contentMode = UIViewContentModeLeft;
imageView.image = [UIImage imageNamed:#"NavBar-iPhone.png"];
[navigationController.navigationBar insertSubview:imageView atIndex:0];
So for your need you would give the -initWithFrame method appropriate values.
static CGFloat const CustomNavigationBarHeight = 74;
#implementation WTNavigationBar
- (CGSize)sizeThatFits:(CGSize)size{
size.width = 1024;
size.height = CustomNavigationBarHeight;
return size;
}
-(void)layoutSubviews {
[super layoutSubviews];
for (UIView *view in self.subviews) {
SFLog(#"view.class=%#",[view class]);
if ([view isKindOfClass:NSClassFromString(#"UINavigationItemButtonView")]) {
float centerY = self.bounds.size.height / 2.0f;
CGPoint center = view.center;
center.y = centerY;
view.center = center;
}
}
}
#end
in my iPad app,which has a fixed landscape orientation,I found I have to hardcode the size's width
I managed to do something similar by subclassing UINavigationBar and overriding -layoutSubviews. The code looks like:
-(void)layoutSubviews {
[super layoutSubviews];
int i = 0;
for (UIView *view in self.subviews) {
NSLog(#"%i. %#", i++, [view description]);
if ([view isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
view.frame = CGRectMake(0, 0, 100, 50);
}
}
}
If you need to know how to subclass UINavigationBar, have a look at this very good answer.
I am not really sure about the NSClassFromString(#"UINavigationButton")] part. It works, but I did this as an experiment, and I'm not sure if this will get approved by Apple. I hope someone with a better knowledge might shed some light.
For the UINavigationbar
In iOS SDK 4.3 and beyond, there is a way (hack) to change the height of the UINavigationBar.
To change the height of UINavigationController, change its frame size in viewWillAppear:animated: function. Then, the height will stay customized throughout whole app.
For the UIBarButtonItems
I've actually run into this myself and the only thing I could come up with was leveraging initWithCustomView and passing in a UIButton with a defined frame.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
/*
* Insert button styling
*/
button.frame = CGRectMake(0, 0, width, height);
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
Otherwise UIBarButtonItem only has a width property that can be set but unfortunately not a height property. Another nifty thing I've done with initWithCustomView is to pass in a toolbar with a button and other things like activity indicators. Hope this helps.
How badly do you want this? And, how thin (or thick) do you want to make your navbar?
One approach would be to set the transform of the navbar to scale and translate it. If you scale it too much the title and button text will look wonky, but if you only need to shave a few pixels you might be allright.
Here's the result of scaling it to be 75% of full height (33 pixels tall):
And the code that produced this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = #"Thin Navigation Bar";
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle: #"Press Me" style:UIBarButtonItemStyleBordered target: nil action: NULL ] autorelease];
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
self.navigationController.navigationBar.transform = CGAffineTransformScale( CGAffineTransformMakeTranslation( 0, -cy / 2.0 ), 1.0, scale ) ;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
CGRect r = self.view.frame;
r.origin.y -= cy;
r.size.height += cy;
self.view.frame = r;
}
Now, this does have a number of problems, which may or may not be solvable. #1 is that you're fighting with the UINavigationController to size and position the navbar and the view-controller views. Animating between view controllers that use this technique is likely going to look weird.
I'd be curious if you could solve the related issues...
One last thought: If you dont use a UINavigationController then there really aren't a whole lot of issues with this other than squished text. Or, you could use a navigation controller but hide the default navbar, and add the thin navbar to each of your child-view controller views. You could even subclass UINavigationBar and set the transform from within:
#interface TSThinNavBar : UINavigationBar
{
}
#end
#implementation TSThinNavBar
// assuming we'll always be constructed from a nib
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder: aDecoder];
if ( self != nil )
{
CGFloat scale = .75;
CGFloat cy = 44.0 - ( 44.0 * scale );
self.transform = CGAffineTransformScale( CGAffineTransformMakeTranslation( 0, -cy / 2.0 ), 1.0, scale ) ;
}
return self;
}
#end
I want to have a pointer to an UIImage view so that I don't have to repeat a lot of my code. I need to choose the image based on which button it is but then at the end I apply the same code to whatever digit. oneDigit, twoDigit, and threeDigit are all #property(nonatomic, retain) in the header file. I want imageView to be able to point to one of these UIImageViews. Right now the UIImageViews are not being removed with removeFromSuperview because imageView is not pointing to the objects but creating its own object..
Currently when a button is pressed a Image is added. I want the previous image removed and the new image drawn according to what button was pressed. The code works how I want if I replace imageView with oneDigit, but that would require me to add more code for each variable.
See simplified example below:
- (void)viewDidLoad
{ ...
UIImageView *oneDigit;
UIImageView *twoDigit;
UIImageView *threeDigit;
}
-(IBAction)pressButton:(id)sender {
UIImage *image;
UIImageView *imageView;
UIButton *btn = (UIButton*)sender;
int value = [btn.titleLabel.text intValue];
switch (value) {
case 10:
case 15:
case 20:
[twoDigit removeFromSuperview]; //Remove old image from screen
imageView = twoDigit; //Update imageView pointer for current case
image = [UIImage imageNamed:#"twoDigit.png"];
break;
case 1:
case 2:
case 3:
case 4:
case 5:
[oneDigit removeFromSuperview];
imageView = oneDigit;
image = [UIImage imageNamed:#"oneDigit.png"];
break;
case 100:
case 200:
case 300:
[threeDigit removeFromSuperview];
imageView = threeDigit;
image = [UIImage imageNamed:#"threeDigit.png"];
break;
default:
break;
}
CGPoint point = btn.frame.origin;
CGFloat xposition = point.x - ((image.size.width - btn.frame.size.width) /2);
CGFloat yposition = point.y - ((image.size.height - btn.frame.size.height) /2);
imageView = [ [ UIImageView alloc ] initWithFrame:CGRectMake(xposition, yposition, image.size.width, image.size.height) ];
imageView.image = image;
imageView.contentMode = UIViewContentModeCenter;
[self.view addSubview:imageView];
}
Assuming that oneDigit, twoDigit and threeDigit are UIImageViews, you can just do something similar to
imageView = oneDigit;
Since both are pointers, setting imageView to oneDigit will simply point imageView to the location of oneDigit. When you set it to the new image, don't autorelease it first, because if it's set to threeDigit for example, it will autorelease threeDigit too.
Also, you don't need to set the image property of imageView manually after setting imageView to the new image, because it's essentially referencing the exact same UIImageView with the same image; it's just under a different name.
Is there an apple-house-made way to get a UISlider with a ProgressView. This is used by many streaming applications e.g. native quicktimeplayer or youtube.
(Just to be sure: i'm only in the visualization interested)
cheers Simon
Here's a simple version of what you're describing.
It is "simple" in the sense that I didn't bother trying to add the shading and other subtleties. But it's easy to construct and you can tweak it to draw in a more subtle way if you like. For example, you could make your own image and use it as the slider's thumb.
This is actually a UISlider subclass lying on top of a UIView subclass (MyTherm) that draws the thermometer, plus two UILabels that draw the numbers.
The UISlider subclass eliminates the built-in track, so that the thermometer behind it shows through. But the UISlider's thumb (knob) is still draggable in the normal way, and you can set it to a custom image, get the Value Changed event when the user drags it, and so on. Here is the code for the UISlider subclass that eliminates its own track:
- (CGRect)trackRectForBounds:(CGRect)bounds {
CGRect result = [super trackRectForBounds:bounds];
result.size.height = 0;
return result;
}
The thermometer is an instance of a custom UIView subclass, MyTherm. I instantiated it in the nib and unchecked its Opaque and gave it a background color of Clear Color. It has a value property so it knows how much to fill the thermometer. Here's its drawRect: code:
- (void)drawRect:(CGRect)rect {
CGContextRef c = UIGraphicsGetCurrentContext();
[[UIColor whiteColor] set];
CGFloat ins = 2.0;
CGRect r = CGRectInset(self.bounds, ins, ins);
CGFloat radius = r.size.height / 2.0;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, CGRectGetMaxX(r) - radius, ins);
CGPathAddArc(path, NULL, radius+ins, radius+ins, radius, -M_PI/2.0, M_PI/2.0, true);
CGPathAddArc(path, NULL, CGRectGetMaxX(r) - radius, radius+ins, radius, M_PI/2.0, -M_PI/2.0, true);
CGPathCloseSubpath(path);
CGContextAddPath(c, path);
CGContextSetLineWidth(c, 2);
CGContextStrokePath(c);
CGContextAddPath(c, path);
CGContextClip(c);
CGContextFillRect(c, CGRectMake(r.origin.x, r.origin.y, r.size.width * self.value, r.size.height));
}
To change the thermometer value, change the MyTherm instance's value to a number between 0 and 1, and tell it to redraw itself with setNeedsDisplay.
This is doable using the standard controls.
In Interface Builder place your UISlider immediately on top of your UIProgressView and make them the same size.
On a UISlider the background horizontal line is called the track, the trick is to make it invisible. We do this with a transparent PNG and the UISlider methods setMinimumTrackImage:forState: and setMaximumTrackImage:forState:.
In the viewDidLoad method of your view controller add:
[self.slider setMinimumTrackImage:[UIImage imageNamed:#"transparent.png"] forState:UIControlStateNormal];
[self.slider setMaximumTrackImage:[UIImage imageNamed:#"transparent.png"] forState:UIControlStateNormal];
where self.slider refers to your UISlider.
I've tested the code in Xcode, and this will give you a slider with an independent progress bar.
Solution that suits my design:
class SliderBuffering:UISlider {
let bufferProgress = UIProgressView(progressViewStyle: .Default)
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
self.minimumTrackTintColor = UIColor.clearColor()
self.maximumTrackTintColor = UIColor.clearColor()
bufferProgress.backgroundColor = UIColor.clearColor()
bufferProgress.userInteractionEnabled = false
bufferProgress.progress = 0.0
bufferProgress.progressTintColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.5)
bufferProgress.trackTintColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
self.addSubview(bufferProgress)
}
}
Create a UISlider:
// 1
// Make the slider as a public propriety so you can access it
playerSlider = [[UISlider alloc] init];
[playerSlider setContinuous:YES];
[playerSlider setHighlighted:YES];
// remove the slider filling default blue color
[playerSlider setMaximumTrackTintColor:[UIColor clearColor]];
[playerSlider setMinimumTrackTintColor:[UIColor clearColor]];
// Chose your frame
playerSlider.frame = CGRectMake(--- , -- , yourSliderWith , ----);
// 2
// create a UIView that u can access and make it the shadow of your slider
shadowSlider = [[UIView alloc] init];
shadowSlider.backgroundColor = [UIColor lightTextColor];
shadowSlider.frame = CGRectMake(playerSlider.frame.origin.x , playerSlider.frame.origin.y , playerSlider.frame.size.width , playerSlider.frame.origin.size.height);
shadowSlider.layer.cornerRadius = 4;
shadowSlider.layer.masksToBounds = YES;
[playerSlider addSubview:shadowSlider];
[playerSlider sendSubviewToBack:shadowSlider];
// 3
// Add a timer Update your slider and shadow slider programatically
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateSlider) userInfo:nil repeats:YES];
-(void)updateSlider {
// Update the slider about the music time
playerSlider.value = audioPlayer.currentTime; // based on ur case
playerSlider.maximumValue = audioPlayer.duration;
float smartWidth = 0.0;
smartWidth = (yourSliderFullWidth * audioPlayer.duration ) / 100;
shadowSlider.frame = CGRectMake( shadowSlider.frame.origin.x , shadowSlider.frame.origin.y , smartWidth , shadowSlider.frame.size.height);
}
Enjoy! P.S. I might have some typos.
Idea 1:
You could easily use the UISlider as a progress view by subclassing it. It responds to methods such as 'setValue:animated:' with which you can set the value (i.e: progress) of the view.
Your only 'restriction' creating what you see in your example is the buffer bar, which you could create by 'creatively' skinning the UISlider (because you can add custom skins to it), and perhaps set that skin programmatically.
Idea 2:
Another (easier) option is to subclass UIProgressView, and create a UISlider inside that subclass. You can skin the UISlider to have a see-through skin (no bar, just the knob visible) and lay it over the UIProgressView.
You can use the UIProgressView for the pre-loading (buffering) and the UISlider for movie control / progress indication.
Seems fairly easy :-)
Edit: to actually answer your question, there is no in-house way, but it would be easy to accomplish with the tools given.
You can do some trick like this, it's more easy and understanding. Just insert the code bellow in your UISlider subclass.
- (void)layoutSubviews
{
[super layoutSubviews];
if (_availableDurationImageView == nil) {
// step 1
// get max length that our "availableDurationImageView" will show
UIView *maxTrackView = [self.subviews objectAtIndex:self.subviews.count - 3];
UIImageView *maxTrackImageView = [maxTrackView.subviews objectAtIndex:0];
_maxLength = maxTrackImageView.width;
// step 2
// get the right frame where our "availableDurationImageView" will place in superView
UIView *minTrackView = [self.subviews objectAtIndex:self.subviews.count - 2];
_availableDurationImageView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:#"MediaSlider.bundle/4_jindu_huancun.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2)]];
_availableDurationImageView.opaque = NO;
_availableDurationImageView.frame = minTrackView.frame;
[self insertSubview:_availableDurationImageView belowSubview:minTrackView];
}
}
- (void)setAvailableValue:(NSTimeInterval)availableValue
{
if (availableValue >=0 && availableValue <= 1) {
// use "maxLength" and percentage to set our "availableDurationImageView" 's length
_availableDurationImageView.width = _maxLength * availableValue;
}
}
Adding on matt's solution, note that as of iOS 7.0, implementing trackRectForBounds: is rendered impossible. Here is my solution to this problem :
In your UISlider subclass, do this :
-(void)awakeFromNib
{
[super awakeFromNib];
UIImage* clearColorImage = [UIImage imageWithColor:[UIColor clearColor]];
[self setMinimumTrackImage:clearColorImage forState:UIControlStateNormal];
[self setMaximumTrackImage:clearColorImage forState:UIControlStateNormal];
}
with imageWithColor as this function :
+ (UIImage*) imageWithColor:(UIColor*)color
{
return [UIImage imageWithColor:color andSize:CGSizeMake(1.0f, 1.0f)];
}
That will properly take care of this annoying trackRectangle.
I spent too much time looking for a solution to this problem, here's hoping that'll save some time to another poor soul ;).
Here is a solution in Objective C. https://github.com/abhimuralidharan/BufferSlider
The idea is to create a UIProgressview as a property in the UISlider subclass and add the required constraints programatically.
#import <UIKit/UIKit.h> //.h file
#interface BufferSlider : UISlider
#property(strong,nonatomic) UIProgressView *bufferProgress;
#end
#import "BufferSlider.h" //.m file
#implementation BufferSlider
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
-(void)setup {
self.bufferProgress = [[UIProgressView alloc] initWithFrame:self.bounds];
self.minimumTrackTintColor = [UIColor redColor];
self.maximumTrackTintColor = [UIColor clearColor];
self.value = 0.2;
self.bufferProgress.backgroundColor = [UIColor clearColor];
self.bufferProgress.userInteractionEnabled = NO;
self.bufferProgress.progress = 0.7;
self.bufferProgress.progressTintColor = [[UIColor blueColor] colorWithAlphaComponent:0.5];
self.bufferProgress.trackTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:2];
[self addSubview:self.bufferProgress];
[self setThumbImage:[UIImage imageNamed:#"redThumb"] forState:UIControlStateNormal];
self.bufferProgress.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1 constant:0.75]; // edit the constant value based on the thumb image
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.bufferProgress attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
[self addConstraints:#[left,right,centerY]];
[self sendSubviewToBack:self.bufferProgress];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setup];
}
return self;
}
#end
for Swift5
First, add tap gesture to slider:
let tap_gesture = UITapGestureRecognizer(target: self, action: #selector(self.sliderTapped(gestureRecognizer:)))
self.slider.addGestureRecognizer(tap_gesture)
Then, implement this function:
#objc func sliderTapped(gestureRecognizer: UIGestureRecognizer) {
let locationOnSlider = gestureRecognizer.location(in: self.slider)
let maxWidth = self.slider.frame.size.width
let touchLocationRatio = locationOnSlider.x * CGFloat(self.slider.maximumValue) / CGFloat(maxWidth)
self.slider.value = Float(touchLocationRatio)
print("New value: ", round(self.slider.value))
}
I am struggeling with my UIScrollview to get it to zoom-in the underlying UIImageView. In my view controller I set
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return myImageView;
}
In the viewDidLoad method I try to set the zoomScale to 2 as follows (note the UIImageView and Image is set in Interface Builder):
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
myScrollView.delegate = self;
NSLog(#"zoomScale: %.1f, minZoolScale: %.3f", myScrollView.zoomScale, myScrollView.minimumZoomScale);
}
I tried a few variations of this, but the NSLog always shows a zoomScale of 1.0.
Any ideas where I screw this one up?
I finally got this to work. what caused the problem was the delegate call being at the end. I now moved it up and .... here we go.
New code looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.delegate = self;
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
}
Here is another example I made. This one is using an image that is included in the resource folder. Compared to the one you have this one adds the UIImageView to the view as a subview and then changes the zoom to the whole view.
-(void)viewDidLoad{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"random.jpg"];
imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
[(UIScrollView *) self.view setContentSize:[image size]];
[(UIScrollView *) self.view setMaximumZoomScale:2.0];
[(UIScrollView *) self.view setMinimumZoomScale:0.5];
}
I know this is quite late as answers go, but the problem is that your code calls zoomScale before it sets the delegate. You are right the other things in there don't require the delegate, but zoomScale does because it has to be able to call back when the zoom is complete. At least that's how I think it works.
My code must be completely crazy because the scale that I use is completely opposite to what tutorials and others are doing. For me, minScale = 1 which indicates that the image is fully zoomed out and fits the UIImageView that contains it.
Here's my code:
[self.imageView setImage:image];
// Makes the content size the same size as the imageView size.
// Since the image size and the scroll view size should be the same, the scroll view shouldn't scroll, only bounce.
self.scrollView.contentSize = self.imageView.frame.size;
// despite what tutorials say, the scale actually goes from one (image sized to fit screen) to max (image at actual resolution)
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat minScale = 1;
// max is calculated by finding the max ratio factor of the image size to the scroll view size (which will change based on the device)
CGFloat scaleWidth = image.size.width / scrollViewFrame.size.width;
CGFloat scaleHeight = image.size.height / scrollViewFrame.size.height;
self.scrollView.maximumZoomScale = MAX(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
// ensure we are zoomed out fully
self.scrollView.zoomScale = minScale;
This works as I expect. When I load the image into the UIImageView, it is fully zoomed out. I can then zoom in and then I can pan the image.