After converting code to latest swift 3.0 I am shown this error.
Also tell me solution for CGSize = CGSizeMake(0,0)
static var frameAtStartOfPan: CGRect = CGRectZero
static var startPointOfPan: CGPoint = CGPointZero
Which is also unavailable.
CGRect Can be simply created using an instance of a CGPoint or CGSize, thats given below.
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 100, height: 100))
// Or
let rect = CGRect(origin: .zero, size: CGSize(width: 100, height: 100))
Or if we want to specify each value in CGFloat or Double or Int, we can use this method.
let rect = CGRect(x: 0, y: 0, width: 100, height: 100) // CGFloat, Double, Int
CGPoint Can be created like this.
let point = CGPoint(x: 0,y :0) // CGFloat, Double, Int
CGSize Can be created like this.
let size = CGSize(width: 100, height: 100) // CGFloat, Double, Int
Also size and point with 0 as the values, it can be done like this.
let size = CGSize.zero // width = 0, height = 0
let point = CGPoint.zero // x = 0, y = 0, equal to CGPointZero
let rect = CGRect.zero // equal to CGRectZero
CGRectZero & CGPointZero replaced with CGRect.zero & CGPoint.zero in Swift 3.0.
Quick fix for CGRectMake , CGPointMake, CGSizeMake in Swift3 & iOS10
Add these extensions :
extension CGRect{
init(_ x:CGFloat,_ y:CGFloat,_ width:CGFloat,_ height:CGFloat) {
self.init(x:x,y:y,width:width,height:height)
}
}
extension CGSize{
init(_ width:CGFloat,_ height:CGFloat) {
self.init(width:width,height:height)
}
}
extension CGPoint{
init(_ x:CGFloat,_ y:CGFloat) {
self.init(x:x,y:y)
}
}
Then go to "Find and Replace in Workspace"
Find CGRectMake , CGPointMake, CGSizeMake and Replace them with CGRect , CGPoint, CGSize
These steps might save all the time as Xcode right now doesn't give us quick conversion from Swift 2+ to Swift 3
In Swift 3, you can simply use CGPoint.zero or CGRect.zero in place of CGRectZero or CGPointZero.
However, in Swift 4, CGRect.zero and 'CGPoint.zero' will work
If you want to use them as in swift 2, you can use these funcs:
For CGRectMake:
func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
return CGRect(x: x, y: y, width: width, height: height)
}
For CGPointMake:
func CGPointMake(_ x: CGFloat, _ y: CGFloat) -> CGPoint {
return CGPoint(x: x, y: y)
}
For CGSizeMake:
func CGSizeMake(_ width: CGFloat, _ height: CGFloat) -> CGSize {
return CGSize(width: width, height: height)
}
Just put them outside any class and it should work. (Works for me at least)
The structures as well as all other symbols in Core Graphics and other APIs have become cleaner and also include parameter names.
https://developer.apple.com/reference/coregraphics#symbols
CGRect(x: Int, y: Int, width: Int, height: Int)
CGVector(dx: Int, dy: Int)
CGPoint(x: Double, y: Double)
CGSize(width: Int, height: Int)
CGPoint Example:
let newPoint = stackMid.convert(CGPoint(x: -10, y: 10), to: self)
CGVector Example:
self.physicsWorld.gravity = CGVector(dx: 0, dy: gravity)
CGSize Example:
hero.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 16, height: 18))
CGRect Example:
let back = SKShapeNode(rect: CGRect(x: 0-120, y: 1024-200-30, width: 240, height: 140), cornerRadius: 20)
Apple Blog
Swift syntax and API renaming changes in Swift 3 make the language feel more natural, and provide an even more Swift-y experience when calling Cocoa frameworks. Popular frameworks Core Graphics and Grand Central Dispatch have new, much cleaner interfaces in Swift.
Swift 3 Bonus: Why didn't anyone mention the short form?
CGRect(origin: .zero, size: size)
.zero instead of CGPoint.zero
When the type is defined, you can safely omit it.
Try the following:
var myToolBar = UIToolbar.init(frame: CGRect.init(x: 0, y: 0, width: 320, height: 44))
This is for the swift's latest version
You can use this with replacement of CGRectZero
CGRect.zero
For CGSize
CGSize(width: self.view.frame.width * 3, height: self.view.frame.size.height)
Instead of CGSizeMake add CGSize it will work.
CGSizeMake is used in objective c.
you don't want to use make with swift
In Swift 4 it works like below
collectionView.setContentOffset(CGPoint.zero, animated: true)
Related
I want to be able to pass a percentage value to a CGRect
I created a Percentage class to get the input and then created four values (x, y, width, height)
Now I need to create an extension for CGRect to be able to use the Percentage values in it. The extension returns an error.
extension CGRect {
init(x: Percentage, y: Percentage, width: Percentage, height: Percentage) {
self.init(x: Percentage.x, y: Percentage.y, width: Percentage.width, height: Percentage.height)
ERROR: Instance member 'x' cannot be used on type 'Percentage'
}
}
class Percentage {
let x: CGFloat
let y: CGFloat
let width: CGFloat
let height: CGFloat
init(_ value: CGFloat) {
self.x = UIScreen.main.bounds.width * value
self.y = UIScreen.main.bounds.height * value
self.width = UIScreen.main.bounds.width * value
self.height = UIScreen.main.bounds.height * value
}
}
Thanks for your help ;)
EDIT:
I wanna be able to use it like this:
CGRect(x: Percentage(0.5), y: 50, width: 100, height: 100)
Instance member 'x' cannot be used on type 'Percentage'
you're using type of class so even if you had in parameter percentage, it wouldn't work.
But better would be if you just passed certain Percentage as parameter for init and then use its properties (use it with small p because you need init parameter, not your class type)
init(percentage: Percentage) {
self.init(x: percentage.x, y: percentage.y, width: percentage.width, height: percentage.height)
}
After your edit I have to say that you don’t have to use custom init and you can just pass property of your Percentage
CGRect(x: yourPercentage.x, y: 50, ...)
Think there is no need for a custom extension/classes - just use:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let viewRect = view.frame
let scaleFactor:CGFloat = 0.5
let scaledFrame = viewRect.applying(CGAffineTransform(scaleX: scaleFactor, y: scaleFactor))
print("Scaled size: \(scaledFrame)")
// Scaled size: (0.0, 0.0, 50.0, 50.0)
However, you also can move this "feature" to an extension as well.
The code is added to Github to let you understand the real problem.
This is the hierarchy:
-- ViewController.View P [width: 375, height: 667]
---- UIImageView A [width: 375, height: 667] Name: imgBackground
[A is holding an image of size(1287,1662)]
---- UIImageView B [width: 100, height: 100] Name: imgForeground
[B is holding an image of size(2400,982)]
I am trying to merge A with B but the result is stretched.
This is the merge code:
func mixImagesWith(frontImage:UIImage?, backgroundImage: UIImage?, atPoint point:CGPoint, ofSize signatureSize:CGSize) -> UIImage {
let size = self.imgBackground.frame.size
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
backgroundImage?.draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height))
frontImage?.draw(in: CGRect.init(x: point.x, y: point.y, width: signatureSize.width, height: signatureSize.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
Note:
.contentMode = .scaleAspectFit
Code works but the result is stretched.
See this line in code, let size = self.imgBackground.frame.size – I need to change this to fix the problem. Find the origin of subview with respect to UIImage size
Here's the screenshot to understand the problem:
What should I do to get the proper output of merge function?
You have two bugs in your code:
You should also calculate aspect for document image to fit it into UIImageView. In mergeImages() replace:
img.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
with:
img.draw(in: getAspectFitFrame(sizeImgView: size, sizeImage: img.size))
When calculating aspect you center image horizontally/vertically if its width/height less then UIImageView width/height. But instead of comparing newWidth and newHeight you should compare factors:
if hfactor > vfactor {
y = (sizeImgView.height - newHeight) / 2
} else {
x = (sizeImgView.width - newWidth) / 2
}
Try bellow code it works for me, hope it works for you too,
func addWaterMarkToImage(img:UIImage, sizeWaterMark:CGRect, waterMarkImage:UIImage, completion : ((UIImage)->())?){
handler = completion
let img2:UIImage = waterMarkImage
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
UIGraphicsBeginImageContext(img.size)
img.draw(in: rect)
let frameAspect:CGRect = getAspectFitFrame(sizeImgView: sizeWaterMark.size, sizeImage: waterMarkImage.size)
let frameOrig:CGRect = CGRect(x: sizeWaterMark.origin.x+frameAspect.origin.x, y: sizeWaterMark.origin.y+frameAspect.origin.y, width: frameAspect.size.width, height: frameAspect.size.height)
img2.draw(in: frameOrig, blendMode: .normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if handler != nil {
handler!(result!)
}
}
//MARK - Get Aspect Fit frame of UIImage
func getAspectFitFrame(sizeImgView:CGSize, sizeImage:CGSize) -> CGRect{
let imageSize:CGSize = sizeImage
let viewSize:CGSize = sizeImgView
let hfactor : CGFloat = imageSize.width/viewSize.width
let vfactor : CGFloat = imageSize.height/viewSize.height
let factor : CGFloat = max(hfactor, vfactor)
// Divide the size by the greater of the vertical or horizontal shrinkage factor
let newWidth : CGFloat = imageSize.width / factor
let newHeight : CGFloat = imageSize.height / factor
var x:CGFloat = 0.0
var y:CGFloat = 0.0
if newWidth > newHeight{
y = (sizeImgView.height - newHeight)/2
}
if newHeight > newWidth{
x = (sizeImgView.width - newWidth)/2
}
let newRect:CGRect = CGRect(x: x, y: y, width: newWidth, height: newHeight)
return newRect
}
My goal is to have a bunch of UIViews that have a property of CGRect falling from offscreen and then piling up. Right now, I have one circle working, but every time I try to add another circle, they won't animate anymore. I am using UIDynamics and its gravity function. Here is my code:
func addBox(location: CGRect) -> UIView {
let newBox = UIView(frame: location)
newBox.backgroundColor = UIColor(red: 186.0/255.0, green: 216.0/255.0, blue: 242.0/255.0, alpha: 1.0)
newBox.layer.cornerRadius = newBox.frame.width/2
hView.insertSubview(newBox, at: 0)
return newBox
}
addBox(location: CGRect(x: 100, y: -100, width: 100, height: 100))
var animator: UIDynamicAnimator? = nil;
let gravity = UIGravityBehavior()
var collision: UICollisionBehavior!
animator = UIDynamicAnimator(referenceView: hView)
func createAnimatorStuff(box: UIView) {
collision = UICollisionBehavior(items: [box])
collision.translatesReferenceBoundsIntoBoundary = true
animator?.addBehavior(collision)
gravity.addItem(box)
gravity.gravityDirection = CGVector(dx: 0, dy: 0.8)
animator?.addBehavior(gravity)
}
func dropDown() {
let xVal = 100
let xVal2 = 700
let box = addBox(location: CGRect(x: xVal, y: 100, width: 100, height: 100))
let box2 = addBox(location: CGRect(x: xVal2, y: 100, width: 100, height: 100))
createAnimatorStuff(box: box)
createAnimatorStuff(box: box2)
}
dropDown()
If anyone could help me and adjust my code to create more circles falling and then piling up I would immensely appreciate it. Truly. Please ask any questions and thank you so much in advance.
(NOTE: the question was updated with these suggestions, so that's why it appears to suggest things that were already done)
The general problem you are having is that you made box a global variable and you keep reinitializing the UIDynamicAnimator. So ...
Remove the box var, then
Change:
func addBox(location: CGRect)
to:
func addBox(location: CGRect) -> UIView
and return newBox at the end.
Then, change:
func createAnimatorStuff() {
to:
func createAnimatorStuff(box: UIView) {
and
addBox(location: CGRect(x: xVal, y: 100, width: 100, height: 100))
createAnimatorStuff()
To
let box = addBox(location: CGRect(x: xVal, y: 100, width: 100, height: 100))
createAnimatorStuff(box: box)
Finally, don't create a new UIDynamicAnimator for every call -- make one and share it. You can do this with
let animator = UIDynamicAnimator(referenceView: hView)
(also, you don't need semicolons in Swift)
var view = UIView(frame: CGRect(x: 5, y: 5, width:50, height: 30))
println(view.frame.size.width)
println(view.frame.width)
They both print out the same value, whats the difference between these two? Are there some specific scenarios where a particular one should be used?
CGRect extension has width and height properties as get method.
struct CGRect {
var origin: CGPoint
var size: CGSize
}
extension CGRect {
var width: CGFloat { get }
var height: CGFloat { get }
}
Basically they are the same the difference is
frame.width = 5 is not possible
frame.size.width = 5 is possible
I have an app where I take a UIBezierPath and use it as a brush by a series of appendPath: calls. After a few goes and with really complex brush shapes the memory runs out and the app grinds to a halt. What I really want to do is a full on union exactly like Paint Code does but I can't find any way of doing this.
How would I go about unioning two or more UIBezierPaths?
EDIT:
Here is a visual of what I want to achieve dynamically.
In Paint Code you take two paths and overlap them like this:
BUT I want to merge / union them into one new single path like:
Note that in the bottom panel of Paint Code there is code for now one single shape and this is what I want to be able to get to programatically with maybe 1000 original paths.
You can get desired result easily by following 2 concepts of Core Graphics :-
i)CGBlendMode
ii)OverLap2Layer
Blend modes tell a context how to apply new content to itself. They determine how pixel data is digitally blended.
class UnionUIBezierPaths : UIView {
var firstBeizerPath:UIImage!
var secondBeizerPath:UIImage!
override func draw(_ rect: CGRect) {
super.draw(rect)
firstBeizerPath = drawOverLapPath(firstBeizerpath: drawCircle(), secondBeizerPath: polygon())
secondBeizerPath = drawOverLapPath(firstBeizerpath: polygon(), secondBeizerPath: drawCircle())
let image = UIImage().overLap2Layer(firstLayer:firstBeizerPath , secondLayer:secondBeizerPath)
}
func drawCircle() -> UIBezierPath {
let path = UIBezierPath(ovalIn: CGRect(x: 40, y: 120, width: 100, height: 100) )
return path
}
func polygon() -> UIBezierPath {
let beizerPath = UIBezierPath()
beizerPath.move(to: CGPoint(x: 100, y: 10) )
beizerPath.addLine(to: CGPoint(x: 200.0, y: 40.0) )
beizerPath.addLine(to: CGPoint(x: 160, y: 140) )
beizerPath.addLine(to: CGPoint(x: 40, y: 140) )
beizerPath.addLine(to: CGPoint(x: 0, y: 40) )
beizerPath.close()
return beizerPath
}
func drawOverLapPath(firstBeizerpath:UIBezierPath ,secondBeizerPath:UIBezierPath ) -> UIImage {
UIGraphicsBeginImageContext(self.frame.size)
let firstpath = firstBeizerpath
UIColor.white.setFill()
UIColor.black.setStroke()
firstpath.stroke()
firstpath.fill()
// sourceAtop = 20
let mode = CGBlendMode(rawValue:20)
UIGraphicsGetCurrentContext()!.setBlendMode(mode!)
let secondPath = secondBeizerPath
UIColor.white.setFill()
UIColor.white.setStroke()
secondPath.fill()
secondPath.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
func drawImage(image1:UIImage , secondImage:UIImage ) ->UIImage
{
UIGraphicsBeginImageContext(self.frame.size)
image1.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height) )
secondImage.draw(in: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height) )
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
//OverLap2Layer
extension UIImage {
func overLap2Layer(firstLayer:UIImage , secondLayer:UIImage ) -> UIImage {
UIGraphicsBeginImageContext(firstLayer.size)
firstLayer.draw(in: CGRect(x: 0, y: 0, width: firstLayer.size.width, height: firstLayer.size.height) )
secondLayer.draw(in: CGRect(x: 0, y: 0, width: firstLayer.size.width, height: firstLayer.size.height) )
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
First Path :-
Second Path :-
Final Result :-
Reference:-
Blend in Core Graphics ,
Creating Image
GitHub Demo
Finally a solution!!
Using https://github.com/adamwulf/ClippingBezier you can find the intersecting points. Then you can walk through the path, turning left if clockwise or vice-versa to stay on the outside. Then you can generate a new path using the sequence of points.
You can use the GPCPolygon, an Objective-C wrapper for GPC
-GPCPolygonSet*) initWithPolygons:(NSMutableArray*)points;
or
- (GPCPolygonSet*) unionWithPolygonSet:(GPCPolygonSet*)p2;