I'm creating a very simple app for iPhone.
Just don't know how to make the button (image) move to random position (but on the screen) when it's touched, in Swift.
I'm using Xcode 6.
Use this for the #IBAction of your button:
#IBAction func moveButton(button: UIButton) {
// Find the button's width and height
let buttonWidth = button.frame.width
let buttonHeight = button.frame.height
// Find the width and height of the enclosing view
let viewWidth = button.superview!.bounds.width
let viewHeight = button.superview!.bounds.height
// Compute width and height of the area to contain the button's center
let xwidth = viewWidth - buttonWidth
let yheight = viewHeight - buttonHeight
// Generate a random x and y offset
let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))
// Offset the button's center by the random offsets.
button.center.x = xoffset + buttonWidth / 2
button.center.y = yoffset + buttonHeight / 2
}
Warning: If you have AutoLayout enabled, your button could snap back to its original location when the subviews are laid out. See the solution to this problem here.
Related
I have one imageview which has 2 gestureRecognizers
1)Pinch
2)Pan
I am able to pinch an image and zoom using scale property
what I am not able to achieve is that when I zoom that image and drag to all 4 sides I can drag image and I am able to see the background view behind the imageview
want to restrict drag of the imageview till that zommed image is shown
Here is the code for pinch and pan gesture
#objc func pinchRecognized(pinch: UIPinchGestureRecognizer) {
if let view = pinch.view {
view.transform = view.transform.scaledBy(x: pinch.scale, y: pinch.scale)
pinch.scale = 1
}
}
#objc func PangestureMethod(gestureRecognizer: UIPanGestureRecognizer){
guard gestureRecognizer.view != nil else {return}
let piece = gestureRecognizer.view!
let translation = gestureRecognizer.translation(in: piece.superview)
if gestureRecognizer.state == .began {
self.initialCenter = piece.center
}
if gestureRecognizer.state != .cancelled {
let newCenter = CGPoint(x: initialCenter.x + translation.x, y: initialCenter.y + translation.y)
piece.center = newCenter
}
else {
piece.center = initialCenter
}
}
First, it's (probably) easier to reset the Pan Gesture's Translation so we're calculating relative movement:
if gestureRecognizer.state != .cancelled {
// translation will be + or - a small number of points
// do what's needed to move the view
// reset recognizer
gestureRecognizer.setTranslation(.zero, in: superV)
}
Otherwise, let's say you drag (pan) right 300-pts, but the view can only move 20-pts, then it won't start moving back to the left until you've dragged 280-pts left.
So, when dragging horizontally and we want to stop when the drag-view is at the left or right edge of its superView...
calculate MAX centerX position
that will be 1/2 of the drag-view's width
calculate MIN centerX position
that will be width-of-superView minus 1/2 of the drag-view's width
As an example, if the superView's width is 100, and the drag-view's width is 200, the MAX centerX will be 100 and the MIN centerX will be Zero.
Then we do the same thing for the centerY position.
Try using this as your Pan Gesture handler:
#objc func PangestureMethod(gestureRecognizer: UIPanGestureRecognizer){
// unwrap the view from the gesture
// AND
// unwrap that view's superView
guard let piece = gestureRecognizer.view,
let superV = piece.superview
else {
return
}
let translation = gestureRecognizer.translation(in: superV)
if gestureRecognizer.state == .began {
self.initialCenter = piece.center
}
if gestureRecognizer.state != .cancelled {
// what the new centerX and centerY will be
var newX: CGFloat = piece.center.x + translation.x
var newY: CGFloat = piece.center.y + translation.y
// MAX centerX is 1/2 the width of the piece's frame
let mxX = piece.frame.width * 0.5
// MIN centerX is Width of superView minus 1/2 the width of the piece's frame
let mnX = superV.bounds.width - piece.frame.width * 0.5
// make sure new centerX is neither greater than MAX nor less than MIN
newX = max(min(newX, mxX), mnX)
// MAX centerY is 1/2 the height of the piece's frame
let mxY = piece.frame.height * 0.5
// MIN centerY is Height of superView minus 1/2 the height of the piece's frame
let mnY = superV.bounds.height - piece.frame.height * 0.5
// make sure new centerY is neither greater than MAX nor less than MIN
newY = max(min(newY, mxY), mnY)
// set the new center
piece.center = CGPoint(x: newX, y: newY)
// reset recognizer
gestureRecognizer.setTranslation(.zero, in: superV)
}
else {
piece.center = initialCenter
}
}
Edit
To prevent the Pinch Gesture from scaling down the view to smaller than its superView frame, we can apply the new scale value from the gesture to a CGRect of the view's frame before applying it to the view.
Then, only apply the scaling to the view if the resulting rect would not be smaller than the superView's frame.
Give this a try:
#objc func pinchRecognized(pinch: UIPinchGestureRecognizer) {
// unwrap the view from the gesture
// AND
// unwrap that view's superView
guard let piece = pinch.view,
let superV = piece.superview
else {
return
}
// this is a bit verbose for clarity
// get the new scale
let sc = pinch.scale
// get current frame of "piece" view
let currentPieceRect = piece.frame
// apply scaling transform to the rect
let futureRect = currentPieceRect.applying(CGAffineTransform(scaleX: sc, y: sc))
// if the resulting rect's width will be
// greater-than-or-equal to superView's width
if futureRect.width >= superV.bounds.width {
// go ahead and scale the piece view
piece.transform = piece.transform.scaledBy(x: sc, y: sc)
}
pinch.scale = 1
}
Hey I implemented marquee label into my project with cocoa pods but have been unsuccessful in getting it to rotate.
let newsFeed: MarqueeLabel = MarqueeLabel(
frame: CGRect(x:15, y:66, width:28, height:159))
newsFeed.textAlignment = .right newsFeed.text = "TEXT"
self.view.addSubview(newsFeed)
newsFeed.transform = CGAffineTransform(
rotationAngle: CGFloat(-(Double.pi / 2.0)))
newsFeed.frame = CGRect(x:15, y:66, width:28, height:159)
You should:
Set the height and width of your field to the unrotated height and
width when you create it:
Not try to manipulate the frame once you've changed the view's
transform. Instead you should set the view's center: To quote Apple's
docs on UIView.transform:
Warning When the value of this property is anything other than the
identity transform, the value in the frame property is undefined and
should be ignored.
I don't know what a MarqueeLabel is, but this code using a vanilla UILabel works:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let startingFrameRect = CGRect(x:15, y:66, width:159, height:28)
let newsFeed = UILabel(
//Create the label wide and short as if it is not rotated.
frame: startingFrameRect)
newsFeed.text = "Some Text"
self.view.addSubview(newsFeed)
newsFeed.transform = CGAffineTransform(
rotationAngle: CGFloat(-(Double.pi / 2.0)))
//Add a border so you can see the label's rotated frame
newsFeed.layer.borderWidth = 1.0
//Adjust the view's center based on it's origin, but exchanging height and width
newsFeed.center = CGPoint (x: startingFrameRect.origin.x + startingFrameRect.size.height / 2,
y: startingFrameRect.origin.y + startingFrameRect.size.width / 2)
//Also add a view to show where the unrotated view would land.
let boxView = UIView(frame: startingFrameRect)
boxView.layer.borderWidth = 1.0
boxView.layer.borderColor = UIColor.blue.cgColor
self.view.addSubview(boxView)
}
}
I've been trying unsuccessfully to vertically center my text in my UITextView as soon as my app starts up. No matter what I've tried the text just appears at the top of the text view like it normally would. Here is a photo.
The only time the text centers correctly is when I type it, this was done by using an observer. Here is the code I'm using to center my text in viewDidLoad, I've tried 2 methods, neither worked.
extension UITextView {
func centerText() {
self.textAlignment = .center
let fittingSize = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
let size = sizeThatFits(fittingSize)
let topOffset = (bounds.size.height - size.height * zoomScale) / 2
let positiveTopOffset = max(1, topOffset)
contentOffset.y = -positiveTopOffset
}
func alignTextVerticallyInContainer() {
var topCorrect = (self.bounds.size.height - self.contentSize.height * self.zoomScale) / 2
topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
self.contentInset.top = topCorrect
}
}
I would call these methods in my viewDidLoad like this: self.goalTextView.centerText() or self.goalTextView.alignTextVerticallyInContainer(). So now that you've seen what I've tried so far, anyone have any idea how to force the text in the textview to be centered vertically on startup? Any help would be appreciated.
I'm trying to move a label to a random location which I have been able to do so using this code.
let buttonWidth = self.samea.frame.width
let buttonHeight = self.samea.frame.height
// Find the width and height of the enclosing view
let viewWidth = self.samea.superview!.bounds.width
let viewHeight = self.samea.superview!.bounds.height
// Compute width and height of the area to contain the button's center
let xwidth = viewWidth - buttonWidth
let yheight = viewHeight - buttonHeight
// Generate a random x and y offset
let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))
// Offset the button's center by the random offsets.
self.newButtonX = xoffset + buttonWidth/2
self.newButtonY = yoffset + buttonHeight/2
self.samea.center.x = self.newButtonX!
self.samea.center.y = self.newButtonY!
The problem is, I have some buttons and labels on the storyboard and I do not want the button to spawn on top of those. Not sure how to do that. Any help is appreciated !
You can create an array property of all the UIButton and UILabel outlets that you have, populate it in your viewDidLoad(_:) method. Then when you're generating your random values, you can put them in a while loop and cycle through the values.
Something like this.
let buttonsAndLabels = [UIView]()
var xoffset: CGFloat
var yoffset: CGFloat
var isOccupied = true
while isOccupied {
xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))
let frame = CGRect(x: xoffset, y: yoffset, width: buttonWidth, height: buttonHeight)
isOccupied = false
for view in buttonsAndLabels {
if view.frame.intersects(frame) {
isOccupied = true
}
}
}
I have been doing it great with the answer in this question:
Strange #IBAction conflict or bug? (Swift)
But since I put iAds, the random position stop working that well, now the button is showed sometimes at his real place in Main.storyboard and sometimes at a random place inside the screen, this is only when iAd is showing
here is the code:
var newButtonX: CGFloat?
var newButtonY: CGFloat?
#IBAction func buttonPressed(sender: AnyObject) {
let buttonWidth = button.frame.width
let buttonHeight = button.frame.height
// Find the width and height of the enclosing view
let viewWidth = button.superview!.bounds.width
let viewHeight = button.superview!.bounds.height
// Compute width and height of the area to contain the button's center
let xwidth = viewWidth - buttonWidth
let yheight = viewHeight - buttonHeight
// Generate a random x and y offset
let xoffset = CGFloat(arc4random_uniform(UInt32(xwidth)))
let yoffset = CGFloat(arc4random_uniform(UInt32(yheight)))
// Offset the button's center by the random offsets.
newButtonX = xoffset + buttonWidth / 2
newButtonY = yoffset + buttonHeight / 2
circle.center.x = newButtonX!
circle.center.y = newButtonY!
}
override func viewDidLayoutSubviews() {
if let buttonX = newButtonX {
button.center.x = buttonX
}
if let buttonY = newButtonY {
button.center.y = buttonY
}
}
Just solve it, I use the iAd bannerview on the Main.storyboard and now it works fine