I found a code to make slide in swift, but cant find, how to make the IMAGE fill the whole screen.
Could you help?
here is the screenshot of slider, and you will see the anchors I placed on it to show you, the whole screen.
and here is the code of it;
import UIKit
class OnboardingController: UIViewController, UIScrollViewDelegate {
let backgroundColor = UIColor(red: 241.0/255.0, green: 196.0/255.0, blue: 15.0/255.0, alpha: 1.0)
let slides = [
[ "image": "book4page1.png"],
[ "image": "book4page2.png"],
[ "image": "book4page3.png"],
]
let screen: CGRect = UIScreen.mainScreen().bounds
var scroll: UIScrollView?
var dots: UIPageControl?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = backgroundColor
scroll = UIScrollView(frame: CGRect(x: 0.0, y: 0.0, width: screen.width, height: screen.height * 0.9))
scroll?.showsHorizontalScrollIndicator = false
scroll?.showsVerticalScrollIndicator = false
scroll?.pagingEnabled = true
view.addSubview(scroll!)
if (slides.count > 1) {
dots = UIPageControl(frame: CGRect(x: 0.0, y: screen.height * 0.875, width: screen.width, height: screen.height * 0.05))
dots?.numberOfPages = slides.count
view.addSubview(dots!)
}
for var i = 0; i < slides.count; ++i {
if let image = UIImage(named: slides[i]["image"]!) {
let imageView: UIImageView = UIImageView(frame: getFrame(image.size.width, iH: image.size.height, slide: i, offset: screen.height * 0.15))
imageView.image = image
scroll?.addSubview(imageView)
}
if let text = slides[i]["text"] {
let textView = UITextView(frame: CGRect(x: screen.width * 0.05 + CGFloat(i) * screen.width, y: screen.height * 0.745, width: screen.width * 0.9, height: 100.0))
textView.text = text
textView.editable = false
textView.selectable = false
textView.textAlignment = NSTextAlignment.Center
textView.font = UIFont.systemFontOfSize(20, weight: 0)
textView.textColor = UIColor.whiteColor()
textView.backgroundColor = UIColor.clearColor()
scroll?.addSubview(textView)
}
}
scroll?.contentSize = CGSizeMake(CGFloat(Int(screen.width) * slides.count), screen.height * 0.5)
scroll?.delegate = self
dots?.addTarget(self, action: Selector("swipe:"), forControlEvents: UIControlEvents.ValueChanged)
let closeButton = UIButton()
closeButton.frame = CGRect(x: screen.width - 70, y: 20, width: 60, height: 60)
closeButton.setTitle("Skip", forState: .Normal)
closeButton.setTitleColor(UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0.5), forState: .Normal)
closeButton.titleLabel!.font = UIFont.systemFontOfSize(16)
closeButton.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
view.addSubview(closeButton)
}
func pressed(sender: UIButton!) {
self.dismissViewControllerAnimated(true) { () -> Void in
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func getFrame (iW: CGFloat, iH: CGFloat, slide: Int, offset: CGFloat) -> CGRect {
let mH: CGFloat = screen.height * 0.50
let mW: CGFloat = screen.width
var h: CGFloat
var w: CGFloat
let r = iW / iH
if (r <= 1) {
h = min(mH, iH)
w = h * r
} else {
w = min(mW, iW)
h = w / r
}
return CGRectMake(
max(0, (mW - w) / 2) + CGFloat(slide) * screen.width,
max(0, (mH - h) / 2) + offset,
w,
h
)
}
func swipe(sender: AnyObject) -> () {
if let scrollView = scroll {
let x = CGFloat(dots!.currentPage) * scrollView.frame.size.width
scroll?.setContentOffset(CGPointMake(x, 0), animated: true)
}
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) -> () {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
dots!.currentPage = Int(pageNumber)
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return UIStatusBarStyle.LightContent
}
}
On your imageView set imageView.contentMode = UIViewContentMode.ScaleAspectFit
Depending on how you want it to scale you should modify the contentMode
of the UIImageView's.
In Objective-C you would do this (it'll be something similar in Swift):
UIImageView * iv = [UIImageView new];
iv.frame = scrollView.bounds;
iv.contentMode = UIViewContentModeScaleAspectFill;
iv.clipsToBounds = true;
iv.image = [UIImage imageNamed:#"image.jpg"];
[scrollView addSubview:iv];
The contentMode is the line you're looking for.
Related
How to draw about three circle in horizontally area with main and ring color in rectangle. I need to create custom button with this circles, something like this:
Is there any good way to do this?
We can design such kind of views with UIStackView in very ease manner.
Take a stackView, set its alignment to center, axis to horizontal and distribution to fill. Create a UILabel/UIButton/UIImageView or even UIView and add rounded radius and border to it. Finally, add those views to the main stackView.
Try this.
override func viewDidLoad() {
super.viewDidLoad()
//Setup stackView
let myStackView = UIStackView()
myStackView.axis = .horizontal
myStackView.alignment = .center
myStackView.distribution = .fillEqually
myStackView.spacing = 8
view.addSubview(myStackView)
//Setup circles
let circle_1 = circleLabel()
let circle_2 = circleLabel()
let circle_3 = circleLabel()
myStackView.addArrangedSubview(circle_1)
myStackView.addArrangedSubview(circle_2)
myStackView.addArrangedSubview(circle_3)
myStackView.translatesAutoresizingMaskIntoConstraints = false
myStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0).isActive = true
myStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0).isActive = true
}
func circleLabel() -> UILabel {
let label = UILabel()
label.backgroundColor = UIColor.red
label.layer.cornerRadius = 12.5
label.layer.masksToBounds = true
label.layer.borderColor = UIColor.orange.cgColor
label.layer.borderWidth = 3.0
label.widthAnchor.constraint(equalToConstant: 25.0).isActive = true
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
return label
}
To make a Single Circle like that, you need to make use of UIBezierPath and CAShapeLayer .
let outerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(50), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let outerCircleShapeLayer = CAShapeLayer()
outerCircleShapeLayer.path = outerCirclePath.cgPath
outerCircleShapeLayer.fillColor = UIColor.white.cgColor
outerCircleShapeLayer.lineWidth = 3.0
view.layer.addSublayer(outerCircleShapeLayer)
// Drawing the inner circle
let innerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(40), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let innerCircleShapeLayer = CAShapeLayer()
innerCircleShapeLayer.path = innerCirclePath.cgPath
innerCircleShapeLayer.fillColor = UIColor.blue.cgColor
view.layer.addSublayer(innerCircleShapeLayer)
I have attached an image below for the Playground version of it .
Just play around with arcCenter and radius values and you will get the desired output
My team helped me and here is solution to create this with dynamically changing state of circles (with different stroke and fill colors):
import UIKit
#IBDesignable
class CirclesButton: UIControl {
#IBInspectable
var firstCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var secondCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var thirdCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
// get context
guard let context = UIGraphicsGetCurrentContext() else { return }
// make configurations
context.setLineWidth(1.0);
context.setStrokeColor(UIColor.white.cgColor)
context.setFillColor(red: 0.0, green: 0.58, blue: 1.0, alpha: 1.0)
// find view center
let dotSize:CGFloat = 11.0
let viewCenter = CGPoint(x: rect.midX, y: rect.midY)
// find personal dot rect
var dotRect = CGRect(x: viewCenter.x - dotSize / 2.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if secondCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find global notes rect
dotRect = CGRect(x: viewCenter.x - dotSize * 1.5 - 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if firstCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find music rect
dotRect = CGRect(x: viewCenter.x + dotSize / 2.0 + 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if thirdCircle {
context.setFillColor(red: 0.0, green: 1.0, blue: 0.04, alpha: 1.0)
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
}
}
It will looks like: CirclesButton
Сode:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let buttonSize: CGFloat = 80
let firstButton = CustomButton(position: CGPoint(x: 0, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(firstButton)
let secondButton = CustomButton(position: CGPoint(x: firstButton.frame.maxX, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(secondButton)
let thirdButton = CustomButton(position: CGPoint(x: secondButton.frame.maxX, y: 0), size: buttonSize, color: .green)
self.view.addSubview(thirdButton)
}
}
class CustomButton: UIButton {
init(position: CGPoint, size: CGFloat, color: UIColor) {
super.init(frame: CGRect(x: position.x, y: position.y, width: size, height: size))
self.backgroundColor = color
self.layer.cornerRadius = size / 2
self.clipsToBounds = true
self.layer.borderWidth = 4.0 // make it what ever you want
self.layer.borderColor = UIColor.white.cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
You can handle button tapped like:
override func viewDidLoad() {
super.viewDidLoad()
firstButton.addTarget(self, action: #selector(handleFirstButton), for: .touchUpInside)
}
#objc func handleFirstButton(sender: UIButton) {
print("first button tapped")
}
Best and Universal Solution for **Button or Label creation (Fully Dynamic)**
var x = 10
var y = 5
var buttonHeight = 40
var buttonWidth = 40
for i in 0..<3 {
let roundButton = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight))
roundButton.setTitle("Butt\(i)", for: .normal)
roundButton.layer.cornerRadius = roundButton.bounds.size.height/2
yourButtonBackView.addSubview(roundButton)
x = x + buttonWidth + 10
if x >= Int(yourButtonBackView.frame.width - 30) {
y = y + buttonHeight + 10
x = 10
}
}
var emitter = CAEmitterLayer()
emitter.emitterPosition = CGPoint(x: self.view.frame.size.width / 2, y: -10)
emitter.emitterShape = kCAEmitterLayerLine
emitter.emitterSize = CGSize(width: self.view.frame.size.width, height: 2.0)
emitter.emitterCells = generateEmitterCells()
self.view.layer.addSublayer(emitter)
here, CAEmitterLayer covers my view... the content of self.view not visible..
Ref. code : https://oktapodi.github.io/2017/05/08/particle-effects-in-swift-using-caemitterlayer.html
I want to set this animation on my view.
I don't know if I understand you correct, but if this is the effect you are looking for:
Then you need to:
Add a "container view" for your your emitter to live in
Create an outlet for that view
set clipsToBounds to true for your container view
Here is my ViewController which produced the above screenshot
import UIKit
enum Colors {
static let red = UIColor(red: 1.0, green: 0.0, blue: 77.0/255.0, alpha: 1.0)
static let blue = UIColor.blue
static let green = UIColor(red: 35.0/255.0 , green: 233/255, blue: 173/255.0, alpha: 1.0)
static let yellow = UIColor(red: 1, green: 209/255, blue: 77.0/255.0, alpha: 1.0)
}
enum Images {
static let box = UIImage(named: "Box")!
static let triangle = UIImage(named: "Triangle")!
static let circle = UIImage(named: "Circle")!
static let swirl = UIImage(named: "Spiral")!
}
class ViewController: UIViewController {
#IBOutlet weak var emitterContainer: UIView!
var emitter = CAEmitterLayer()
var colors:[UIColor] = [
Colors.red,
Colors.blue,
Colors.green,
Colors.yellow
]
var images:[UIImage] = [
Images.box,
Images.triangle,
Images.circle,
Images.swirl
]
var velocities:[Int] = [
100,
90,
150,
200
]
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
view.backgroundColor = UIColor.red
emitter.emitterPosition = CGPoint(x: emitterContainer.frame.size.width / 2, y: -10)
emitter.emitterShape = kCAEmitterLayerLine
emitter.emitterSize = CGSize(width: emitterContainer.frame.size.width, height: 2.0)
emitter.emitterCells = generateEmitterCells()
emitterContainer.layer.addSublayer(emitter)
emitterContainer.clipsToBounds = true
}
private func generateEmitterCells() -> [CAEmitterCell] {
var cells:[CAEmitterCell] = [CAEmitterCell]()
for index in 0..<16 {
let cell = CAEmitterCell()
cell.birthRate = 4.0
cell.lifetime = 14.0
cell.lifetimeRange = 0
cell.velocity = CGFloat(getRandomVelocity())
cell.velocityRange = 0
cell.emissionLongitude = CGFloat(Double.pi)
cell.emissionRange = 0.5
cell.spin = 3.5
cell.spinRange = 0
cell.color = getNextColor(i: index)
cell.contents = getNextImage(i: index)
cell.scaleRange = 0.25
cell.scale = 0.1
cells.append(cell)
}
return cells
}
private func getRandomVelocity() -> Int {
return velocities[getRandomNumber()]
}
private func getRandomNumber() -> Int {
return Int(arc4random_uniform(4))
}
private func getNextColor(i:Int) -> CGColor {
if i <= 4 {
return colors[0].cgColor
} else if i <= 8 {
return colors[1].cgColor
} else if i <= 12 {
return colors[2].cgColor
} else {
return colors[3].cgColor
}
}
private func getNextImage(i:Int) -> CGImage {
return images[i % 4].cgImage!
}
}
Hope that helps you.
It is working fine check output of my simulator. Background images are added from storyboard and blue color is done by code. Still working fine.
OUTPUT:
You can fix your problem by changing the way you add the layer right now your adding it on top of everything which sometimes hide other layers and view objects.
change
self.view.layer.addSublayer(emitter)
To
self.view.layer.insertSublayer(emitter, at: 0)
Hello change emitterPosition X of each like below:-
let emitter1 = Emitter.getEmitter(with: #imageLiteral(resourceName: "img_ribbon_4"), directionInRadian: (180 * (.pi/180)), velocity: 50)
emitter1.emitterPosition = CGPoint(x: self.view.frame.width / 3 , y: 0)
emitter1.emitterSize = CGSize(width: self.view.frame.size.width, height: 2.0)
self.view.layer.addSublayer(emitter1)
let emitter2 = Emitter.getEmitter(with: #imageLiteral(resourceName: "img_ribbon_6"), directionInRadian: (180 * (.pi/180)), velocity: 80)
emitter2.emitterPosition = CGPoint(x: self.view.frame.width / 2, y: 0)
emitter2.emitterSize = CGSize(width: self.view.frame.size.width, height: 2.0)
self.view.layer.addSublayer(emitter2)
I hope it will help you,
thank you.
CAReplicator did not keep the state after the switch vc:
Dots of CAReplicator did not keep its scale after the vc switch back.
As you see, the circle animation is created by CAReplicator.
after the main vc switch to another vc, then switch back, the Circle's dots become very small. witch is set in the initial.
My code is below:
In the main vc:
func initUI() {
let lml_frame = CGRect.init(x: 0, y: 64, width: self.view.bounds.size.width, height: 400)
lml_digtal_view = LMLDigitalDazzleAnimationView.init(frame: lml_frame)
self.view.addSubview(lml_digtal_view!)
}
In the LMLDigitalDazzleAnimationView:
import Foundation
import UIKit
class LMLDigitalDazzleAnimationView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
var initFrame = CGRect.init(x: 0, y: 0, width: 320, height: 480)
var fromColor = UIColor.init(red: 240/255.0, green: 77.0/255.0, blue: 48.0/255.0, alpha: 1.0).cgColor
var toColor = UIColor.init(red: 220.0/255.0, green: 28.0/255.0, blue: 44.0/255.0, alpha: 1.0).cgColor
var money:Float? = 1200.25 {
didSet {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
initFrame = frame
initUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initUI(){
let gradul_layer = CAGradientLayer.init()
gradul_layer.frame = CGRect.init(x: 0, y: 0, width: initFrame.width, height: initFrame.height)
gradul_layer.colors = [
fromColor,
toColor
]
gradul_layer.startPoint = CGPoint.init(x: 0.5, y: 0.3)
gradul_layer.endPoint = CGPoint.init(x: 0.5, y: 0.7)
layer.addSublayer(gradul_layer)
let wave_view0 = KHWaveView.init(frame: CGRect.init(x: 0, y: initFrame.height - 80, width: initFrame.width, height: 80))
//wave_view.backgroundColor = UIColor.white
wave_view0.waveColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 0.5)
wave_view0.waveSpeed = 1.3
wave_view0.waveTime = 0
wave_view0.wave()
self.addSubview(wave_view0)
let wave_view = KHWaveView.init(frame: CGRect.init(x: 0, y: initFrame.height - 80, width: initFrame.width, height: 80))
//wave_view.backgroundColor = UIColor.white
wave_view.waveColor = UIColor.white
wave_view.waveSpeed = 1.0
wave_view.waveTime = 0
wave_view.wave()
self.addSubview(wave_view)
animateCircle()
animateDigitalIcrease(money: money!)
}
func animateCircle() -> Void {
let r = CAReplicatorLayer()
r.bounds = CGRect(x:0.0, y:0.0, width:260.0, height:260.0)
r.cornerRadius = 10.0
r.backgroundColor = UIColor.clear.cgColor
r.position = CGPoint.init(x: self.bounds.width / 2.0, y: 160)
self.layer.addSublayer(r)
let dot = CALayer()
dot.bounds = CGRect(x:0.0, y :0.0, width:6.0, height:6.0)
dot.position = CGPoint(x:100.0, y:10.0)
dot.backgroundColor = UIColor(white:1, alpha:1.0).cgColor
dot.cornerRadius = 3.0
r.addSublayer(dot)
let nrDots: Int = 32
r.instanceCount = nrDots
let angle = CGFloat(2*M_PI) / CGFloat(nrDots)
r.instanceTransform = CATransform3DMakeRotation(angle, 0.1, 0.1, 1.0)
let duration:CFTimeInterval = 1.5
let shrink = CABasicAnimation(keyPath: "transform.scale")
shrink.fromValue = 1.0
shrink.toValue = 1.0 // 0.5
shrink.duration = duration
shrink.repeatCount = Float.infinity
dot.add(shrink, forKey: nil)
r.instanceDelay = duration/Double(nrDots)
dot.transform = CATransform3DMakeScale(0.1, 0.1, 0.1)
delay(delay: duration) {
let turn_key_path = "transform.rotation"
let turn_ani = CABasicAnimation.init(keyPath: turn_key_path)
turn_ani.isRemovedOnCompletion = false
turn_ani.fillMode = kCAFillModeForwards
turn_ani.toValue = M_PI*2
turn_ani.duration = 2.0
turn_ani.repeatCount = 2
r.add(turn_ani, forKey: turn_key_path)
}
}
func delay(delay:Double, closure:#escaping ()->()){
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func animateDigitalIcrease(money :Float){
let frame = CGRect.init(x: 0, y: 0, width: 120, height: 80)
let counterLabel = LMLDigitalIncreaseLabel.init(frame: frame, andDuration: 2.0, andFromValue: 0, andToValue: money)
counterLabel?.center = CGPoint.init(x: self.bounds.size.width / 2.0, y: 130)
self.addSubview(counterLabel!)
counterLabel?.start()
delay(delay: 5.0) {
counterLabel?.stop()
self.animateFadeShowSmallMoney()
}
}
func animateFadeShowSmallMoney(){
let border_view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 30))
border_view.layer.cornerRadius = 15
border_view.layer.masksToBounds = true
border_view.layer.borderWidth = 1
border_view.backgroundColor = UIColor.clear
border_view.layer.borderColor = UIColor.white.cgColor
let small_money_frame = CGRect.init(x: 0, y: 0, width: 80, height: 30)
let small_money = UILabel.init(frame: small_money_frame)
small_money.center = border_view.center
small_money.adjustsFontSizeToFitWidth = true
small_money.textAlignment = NSTextAlignment.center
small_money.text = "mo:" + String(format:"%.2f", money!)
small_money.textColor = UIColor.white
border_view.addSubview(small_money)
border_view.alpha = 0.0
self.addSubview(border_view)
border_view.center = CGPoint.init(x: self.bounds.size.width/2.0, y: 220)
UIView.animate(withDuration: 1.0) {
border_view.alpha = 1.0
}
}
}
My code is not good, you can advice me how to encapsulate a animation class better.
After many attention, I solve my issue:
delay(delay: duration) {
let turn_key_path = "transform.rotation"
let turn_ani = CABasicAnimation.init(keyPath: turn_key_path)
turn_ani.isRemovedOnCompletion = false
turn_ani.fillMode = kCAFillModeForwards
turn_ani.toValue = M_PI*2
turn_ani.duration = 2.0
turn_ani.repeatCount = 2
r.add(turn_ani, forKey: turn_key_path)
dot.transform = CATransform3DMakeScale(1, 1, 1) // add this line solve my issue.
}
I am currently messing around with IBDesignable Views, and I am curious if anyone has been able to solve this. I would like to have views added through the interface builder be automatically arranged using a custom layout algorithm within my subview. The view works great when I run the app, but in the interface builder, the views do not rearrange in real time.
I have tried debugging my UIView class, but it seems at all times when the interface builder is initializing the element, it thinks it has zero subviews. It seems the interface builder does not give you a chance to arrange these views after the fact. However, I'm wondering if maybe there is just something I'm missing. Is it possible to rearrange subviews added from the interface builder within an IBDesignable class, and have the views show up rearranged in the interface builder?
Try using the provided method for a custom view and IBDesignable if you are not already. You also might need to refresh your views in Xcode or have it automatically refresh views. Below is the function you may be missing. This is never called in a live app. It is only called in Xcode IB.
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
}
In this instance setUpView is laying out my subviews.
Here is an example that I made. https://github.com/agibson73/ICONButton
import UIKit
#IBDesignable class AGIconButton: UIControl {
private var iconImageView : UIImageView!
private var iconLabel : UILabel!
private var mainSpacer : UIView!
private var highlightView:UIView!
private var widthContraint : NSLayoutConstraint!
var padding : CGFloat = 5
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
//only called at design time
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setUpView()
addTarget(self, action: #selector(AGIconButton.userDidTouchDown), for: .touchDown)
addTarget(self, action: #selector(AGIconButton.userDidTouchUp), for: .touchUpInside)
addTarget(self, action: #selector(AGIconButton.userDidTouchUpOutside), for: .touchUpOutside)
}
#IBInspectable var iconImage: UIImage = UIImage() {
didSet {
iconImageView.image = iconImage
}
}
#IBInspectable var imageSize: CGFloat = 40 {
didSet {
setUpView()
}
}
#IBInspectable var imagePadding: CGFloat = 10 {
didSet {
setUpView()
}
}
#IBInspectable var iconText: String = "Icon Button Time" {
didSet {
setUpView()
}
}
#IBInspectable var iconTextSize: CGFloat = 15 {
didSet {
setUpView()
}
}
#IBInspectable var iconTextColor: UIColor = UIColor.black {
didSet {
setUpView()
}
}
#IBInspectable var alignment: Int = 1 {
didSet {
setUpView()
}
}
override var intrinsicContentSize: CGSize {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: iconTextSize)
label.text = iconText
label.sizeToFit()
return CGSize(width: imageSize + label.frame.width + imagePadding + (padding * 2), height: CGFloat(max(label.frame.height, imageSize) + padding * 2))
}
#IBInspectable var highLightColor: UIColor = UIColor.lightGray {
didSet {
setUpView()
}
}
#IBInspectable var shouldBounce: Bool = true
#IBInspectable var borderColor: UIColor = UIColor.clear {
didSet {
layer.borderColor = borderColor.cgColor
}
}
#IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
#IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
private func setUpView(){
if iconImageView == nil{
iconImageView = UIImageView(image: iconImage)
iconImageView.contentMode = .scaleAspectFit
iconImageView.isUserInteractionEnabled = false
self.addSubview(iconImageView)
}
if mainSpacer == nil{
mainSpacer = UIView(frame: CGRect(x: 0, y: 0, width: imagePadding, height: self.bounds.height))
mainSpacer.isUserInteractionEnabled = false
self.addSubview(mainSpacer)
}
if iconLabel == nil{
iconLabel = UILabel()
iconLabel.isUserInteractionEnabled = false
self.addSubview(iconLabel)
}
if highlightView == nil{
highlightView = UIView(frame: self.bounds)
highlightView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
highlightView.alpha = 0
highlightView.isUserInteractionEnabled = false
self.addSubview(highlightView)
self.bringSubview(toFront: highlightView)
}
highlightView.backgroundColor = highLightColor
iconLabel.font = UIFont.systemFont(ofSize: iconTextSize)
iconLabel.text = iconText
iconLabel.textColor = iconTextColor
iconLabel.sizeToFit()
var usedWidth : CGFloat = self.intrinsicContentSize.width
if bounds.width < usedWidth{
usedWidth = bounds.width
}
let maxImageHeight = min(self.bounds.height - padding, imageSize)
//resize iconlabel if we have to
if maxImageHeight + imagePadding + iconLabel.bounds.width + padding * 2 > usedWidth{
iconLabel.frame = CGRect(x: 0, y: 0, width: self.bounds.width - iconImageView.bounds.width - imagePadding - padding * 2, height: iconLabel.bounds.height)
iconLabel.fitFontForSize(minFontSize: 1, maxFontSize: iconTextSize, accuracy: 1.0)
}
let maxWidth = (self.bounds.width - iconLabel.bounds.width - maxImageHeight - imagePadding) / 2
switch alignment {
case 0:
//intrinsic left
iconImageView.frame = CGRect(x:padding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxImageHeight + padding, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxImageHeight + imagePadding + padding, y: 0, width: iconLabel.frame.width, height: bounds.height)
break
case 1:
//intrinsic center
iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
break
case 2:
//intrinsic icon right text aligned right
iconLabel.frame = CGRect(x: maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
iconLabel.textAlignment = .right
mainSpacer.frame = CGRect(x: iconLabel.frame.width + maxWidth, y: 0, width: imagePadding, height: self.bounds.height)
iconImageView.frame = CGRect(x: iconLabel.frame.width + imagePadding + maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
break
case 3:
//intrinsic center invert icon
iconLabel.frame = CGRect(x:maxWidth, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
mainSpacer.frame = CGRect(x: maxWidth + iconLabel.bounds.width, y: 0, width: imagePadding, height: self.bounds.height)
iconImageView.frame = CGRect(x: maxWidth + iconLabel.bounds.width + imagePadding, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
break
default:
//intrinsic center
iconImageView.frame = CGRect(x: maxWidth, y: self.bounds.midY - maxImageHeight/2,width:maxImageHeight, height: maxImageHeight)
mainSpacer.frame = CGRect(x: maxWidth + maxImageHeight, y: 0, width: imagePadding, height: self.bounds.height)
iconLabel.frame = CGRect(x: maxWidth + maxImageHeight + imagePadding, y: 0, width: iconLabel.frame.width, height: self.bounds.height)
}
}
//layout subviews
override func layoutSubviews() {
super.layoutSubviews()
setUpView()
}
//MARK: Touch Events
//TODO: run on timer to simulate a real press
func userDidTouchDown(){
if shouldBounce == true{
animateBouncyDown()
}else{
self.animateHighlightTo(alpha: 0.3)
}
}
func userDidTouchUp(){
if shouldBounce == true{
animateBouncyUp()
}else{
self.animateHighlightTo(alpha: 0)
}
}
func userDidTouchUpOutside(){
if shouldBounce == true{
animateBouncyUp()
}else{
self.animateHighlightTo(alpha: 0)
}
}
func animateHighlightTo(alpha:CGFloat){
UIView.animate(withDuration: 0.2, animations: { [weak self] in
self?.highlightView.alpha = alpha
})
}
func animateBouncyDown(){
self.transform = CGAffineTransform.identity
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.transform = CGAffineTransform(scaleX: 0.85, y: 0.85)
})
}
func animateBouncyUp(){
UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {[weak self] in
if self != nil{
self?.transform = CGAffineTransform.identity
}
}, completion: nil)
}
}
extension UILabel {
func fitFontForSize( minFontSize : CGFloat = 1.0, maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
var maxFontSize = maxFontSize
var minFontSize = minFontSize
assert(maxFontSize > minFontSize)
layoutIfNeeded() // Can be removed at your own discretion
let constrainedSize = bounds.size
while maxFontSize - minFontSize > accuracy {
let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
font = font.withSize(midFontSize)
sizeToFit()
let checkSize : CGSize = bounds.size
if checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
minFontSize = midFontSize
} else {
maxFontSize = midFontSize
}
}
font = font.withSize(minFontSize)
sizeToFit()
layoutIfNeeded()
}
}
There doesn't seem to be any way to do this. If you drop a custom control into another xib and add subviews to the custom control in Interface Builder, those subviews appear at the same level as the custom control in the view hierarchy when you load the xib. It looks like custom controls can not act as containers in other xibs.
I created the textView and Button(reply) programmatically and I want to move it down to the bottom of my ViewController. It's now being placed under the label(Joshyjosh) and it shouldn't do that. Is there something I need to add to my code or change so it can move to the bottom?
import UIKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextViewDelegate {
var commentView: UITextView?
var footerView: UIView?
var contentHeight: CGFloat = 0
let FOOTERHEIGHT : CGFloat = 50;
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
if self.footerView != nil {
return self.footerView!.bounds.height
}
return FOOTERHEIGHT
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: FOOTERHEIGHT))
footerView?.backgroundColor = UIColor(red: 243.0/255, green: 243.0/255, blue: 243.0/255, alpha: 1)
commentView = UITextView(frame: CGRect(x: 10, y: 5, width: tableView.bounds.width - 80 , height: 40))
commentView?.backgroundColor = UIColor.whiteColor()
commentView?.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)
commentView?.layer.cornerRadius = 2
commentView?.scrollsToTop = true
footerView?.addSubview(commentView!)
let button = UIButton(frame: CGRect(x: tableView.bounds.width - 65, y: 10, width: 60 , height: 30))
button.setTitle("Reply", forState: UIControlState.Normal)
button.backgroundColor = UIColor(red: 155.0/255, green: 189.0/255, blue: 113.0/255, alpha: 1)
button.layer.cornerRadius = 5
button.addTarget(self, action: "reply", forControlEvents: UIControlEvents.TouchUpInside)
footerView?.addSubview(button)
commentView?.delegate = self
return footerView
}
func textViewDidChange(textView: UITextView) {
if (contentHeight == 0) {
contentHeight = commentView!.contentSize.height
}
if(commentView!.contentSize.height != contentHeight && commentView!.contentSize.height > footerView!.bounds.height) {
UIView.animateWithDuration(0.2, animations: { () -> Void in
let myview = self.footerView
print(self.commentView!.contentSize.height)
print(self.commentView?.font!.lineHeight)
let newHeight : CGFloat = self.commentView!.font!.lineHeight
let myFrame = CGRect(x: myview!.frame.minX, y: myview!.frame.minY - newHeight , width: myview!.bounds.width, height: newHeight + myview!.bounds.height)
myview?.frame = myFrame
let mycommview = self.commentView
let newCommHeight : CGFloat = self.commentView!.contentSize.height
let myCommFrame = CGRect(x: mycommview!.frame.minX, y: mycommview!.frame.minY, width: mycommview!.bounds.width, height: newCommHeight)
mycommview?.frame = myCommFrame
self.commentView = mycommview
self.footerView = myview
for item in self.footerView!.subviews {
if(item.isKindOfClass(UIButton.self)){
let button = item as! UIButton
let newY = self.footerView!.bounds.height / 2 - button.bounds.height / 2
let buttonFrame = CGRect(x: button.frame.minX, y: newY , width: button.bounds.width, height : button.bounds.height)
button.frame = buttonFrame
}
}
})
print(self.footerView?.frame)
print(self.commentView?.frame)
contentHeight = commentView!.contentSize.height
}
}