Importing animation function from custom swift file failed - ios

I was creating animations for buttons using extension in swift but when i call the animation function it generates error.
import UIKit
extension UIButton{
func wiggle() {
let wiggleAnim = CABasicAnimation(keyPath: "psoition")
wiggleAnim.duration = 0.05
wiggleAnim.repeatCount = 5
wiggleAnim.autoreverses = true
wiggleAnim.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
wiggleAnim.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
layer.add(wiggleAnim, forKey: "position")
}
}
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var colorizeBtn: UIButton!
#IBOutlet weak var wiggleBtn: UIButton!
#IBOutlet weak var dimBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func colorizeBtnWasPressed(_ sender: Any) {
}
#IBAction func wiggleBtnWasPressed(_ sender: Any) {
wiggleBtn.wiggle()
}
#IBAction func dimBtnwasPressed(_ sender: Any) {
}
}
View Controller

Your wiggleBtn is of type UIView but you write an extension to UIButton.
Either change the extension to UIView or change the type of wiggleBtn to UIButton.

Related

How do you exclude a specific UIButton from an extension in Swift?

I am a beginner so bear with me.
I created a basic timer app and rounded the corners of two buttons. The button was still clickable outside of the circle, I needed to fix that so I found a solution online that said to insert the code into the UIButton's subclass or extension. I found solutions for both ways and chose extension option because the code was easier to read and I could understand it better.
The issue now is that I have a third UIButton that is being affected by the extension and I would like to exclude it. I'm not sure if this is even practical (or possible) so please correct me if there is a better way to approach this. The button I need to exclude from the extension is resetButton.
import UIKit
extension UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
//exclude resetButton???
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var stopButton: UIButton!
#IBOutlet weak var resetButton: UIButton!
var timeRemaining: Int = 10
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//// Moved to an extension in order to
//// remove the clickable areas outside
//// of the circle
//startButton.layer.cornerRadius = 50.0
//stopButton.layer.cornerRadius = 50.0
}
#IBAction func start(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
}
#IBAction func stop(_ sender: Any) {
timer?.invalidate()
}
#IBAction func reset(_ sender: Any) {
timer?.invalidate()
timeRemaining = 10
label.text = "\(timeRemaining)"
}
#objc func step() {
if timeRemaining > 0 {
timeRemaining -= 1
} else {
timer?.invalidate()
}
label.text = "\(timeRemaining)"
}
}
For your case, extension is not the right option as the methods invoked from extension will apply to the type itself (all UIButton objects in this case).
One option for you is to make a subclass of UIButton instead of extension. Something like this:
class RoundedButton: UIButton {
open override func draw(_ rect: CGRect) {
self.layer.cornerRadius = 50.0
self.layer.masksToBounds = true
}
private var touchPath: UIBezierPath {return UIBezierPath(ovalIn: self.bounds)}
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return touchPath.contains(point)
}
}
And hence you can select the buttons that should inherit the custom layout from the class above:
#IBOutlet weak var startButton: RoundedButton!
#IBOutlet weak var stopButton: RoundedButton!
#IBOutlet weak var resetButton: UIButton! // Will not get the style applied for startButton and stopButton

The image is not from one VC to another VC

The image is not from one VC to another VC.
The problem is displaying it to the image view in the main UIViewController. I have everything hooked up correctly in the Storyboard.
Click here to view my storyboard layout.
Please note some of my unnecessary code has been removed from both ViewController classes.
Here is my first UIViewController:
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var imagePicker: UIImagePickerController!
#IBAction func editPhoto(_ sender: UIButton) {
let ViewController2 = storyboard?.instantiateViewController(withIdentifier: "AddNewEaterySegue") as! ViewController2
ViewController2.imageForEdit = imageView.image!
print(imageView)
print(imageView.image!)
navigationController?.pushViewController(ViewController2, animated: true)
}
#IBOutlet weak var imageView: UIImageView! {
didSet {
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
}
Here is my second UIViewController:
import UIKit
class ViewController2: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var filter : CIFilter!
var imageForEdit = UIImage()
var imagePicker: UIImagePickerController!
#IBOutlet weak var select: UISegmentedControl!
#IBOutlet weak var imageLabel: UIImageView!
#IBOutlet weak var textField: UITextField!
#IBAction func saveButtonPressed(_ sender: UIImage) {
if textField.text == "" {
print("Не все поля заполнены")
} else {
}
performSegue(withIdentifier: "unwindSegueFromViewController", sender: sender)
}
override func viewDidLoad() {
super.viewDidLoad()
imageLabel.image = imageForEdit
print(imageLabel)
// Do any additional setup after loading the view.
}
#IBAction func tappedEnter(_ sender: Any) {
if textField.text?.isEmpty ?? true {
return
} else {
if let texttxt = textField.text {
let data = texttxt.data(using: .ascii, allowLossyConversion: false)
if select.selectedSegmentIndex == 0
{
filter = CIFilter(name: "CICode128BarcodeGenerator")
} else {
filter = CIFilter(name: "CIQRCodeGenerator")
}
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 5, y: 5)
let image = UIImage(ciImage: filter.outputImage!.transformed(by: transform))
imageLabel.image = image
}
}
}
}
When you are pushing to ViewController2 your imageView in ViewController is empty.
Where exactly are you setting the image for imageView?
In your storyboard, the imageView in ViewController is empty and also in the didSet method for imageView in ViewController you have not set any image to the imageView.
Try this in ViewController:
#IBOutlet weak var imageView: UIImageView! {
didSet {
//Add a test image to your project.
imageView.image = UIImage(named: "YOUR_TEST_IMAGE_NAME.jpeg")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
}
}
EDIT: Ok I guess I was not clear on your question. So in order to send the image from "ViewController2" back to "ViewController", you can use many methods but the way I would do it is by using Protocols.
I have done it in a generic way but you can apply this to send any kind of data from one view controller back to the previous view controller.
Try this in ViewController:
import UIKit
protocol ImageViewProtocol{
func sendImageToViewController(theImage: UIImage)
}
class ViewController: UIViewController, ImageViewProtocol {
func sendImageToViewController(theImage: UIImage) {
imageView.image = theImage
}
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let viewcontroller2 = storyboard?.instantiateViewController(withIdentifier: "viewcontroller2") as! ViewController2
viewcontroller2.delegate = self
self.navigationController?.pushViewController(viewcontroller2, animated: true)
}
}
Try this in ViewController2:
import UIKit
class ViewController2: UIViewController {
var delegate: ImageViewProtocol!
#IBOutlet weak var barcodeImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
barcodeImageView.image = UIImage(named: "test_image.jpg")
}
#IBAction func saveButtonAction(_ sender: Any) {
delegate.sendImageToViewController(theImage: barcodeImageView.image!)
self.navigationController?.popViewController(animated: true)
}
}

How to update the layout (position) of a popover when its source view changes theirs in Swift?

I want to update the layout of a popover, I've already tried using the setNeedsLayout() and layoutIfNeeded() methods but couldn't find a solution.
I have a simple screen with 4 buttons that call a popover, within the popover there are 2 extra buttons:
Button -> hides button3, and moves button1 and button2 to the left.
reposition -> changes the sourceview of the popover from the button1/2/3/4 (sender) to button4.
Picture of the layout
The problem I have is that I'm not able to update/refresh the screen, and the popover keeps pointing to the former source view position.
Picture of the popover
Somehow I need a method that updates my view and which is stronger than layoutIfNeeded() because if the screen is changed from portrait/landscape the popover changes automatically. Here I changed portrait/landscape/portrait
GitHub of the project. GitHub
The code used is the following:
class ViewController: UIViewController, ButtonDidDisappearDelegate {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
#IBOutlet weak var button4: UIButton!
#IBOutlet weak var constrain2: NSLayoutConstraint!
var popVC: PopVC?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPopover(_ sender: UIButton) {
configureAndPresentPopover(from: sender)
}
func buttonDisappear() {
self.constrain2.constant = 100
self.button3.isHidden = !self.button3.isHidden
if !self.button3.isHidden {
self.constrain2.constant = 10
}
}
func repositionPopover() {
DispatchQueue.main.async {
if let popVC = self.popVC {
self.self.popoverPresentationController(popVC.popoverPresentationController!, willRepositionPopoverTo: self.button4.bounds, in: self.button4)
}
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
}
extension ViewController: UIPopoverPresentationControllerDelegate {
func configureAndPresentPopover(from sender: UIButton) {
popVC = storyboard?.instantiateViewController(withIdentifier: "popVC") as? PopVC
guard let popVC = popVC else {
return
}
popVC.popOverDelegate = self
popVC.modalPresentationStyle = .popover
let popOverVC = popVC.popoverPresentationController
popOverVC?.delegate = self
popOverVC?.sourceView = sender
popOverVC?.sourceRect = sender.bounds
popVC.preferredContentSize = CGSize(width: 300, height: 60)
popOverVC?.permittedArrowDirections = .down
self.present(popVC, animated: true)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController,
willRepositionPopoverTo rect: CGRect,
in view: UIView) {
popoverPresentationController.sourceView = view
popoverPresentationController.sourceRect = rect
}
}
and the popover controller:
protocol ButtonDidDisappearDelegate: class {
func configureAndPresentPopover(from sender: UIButton)
func buttonDisappear()
func repositionPopover()
}
class PopVC: UIViewController {
weak var popOverDelegate: ButtonDidDisappearDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var popoverButton: UIButton!
#IBAction func popoverAction(_ sender: Any) {
popOverDelegate?.buttonDisappear()
DispatchQueue.main.async {
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
#IBAction func reposition(_ sender: Any) {
popOverDelegate?.repositionPopover()
}
}
You need to specify the new position of the arrow and ask for the layout of the presentation controller's view, not only the presented view.
Try :
func repositionPopover() {
let popOverController = presentedViewController?.presentationController as? UIPopoverPresentationController
popOverController?.sourceView = button4
popOverController?.containerView?.setNeedsLayout()
popOverController?.containerView?.layoutIfNeeded()
}
You can try setting the sourceview and sourcerect once again. call this function when the sourcview bounds are changed.

Swift Delegate is not being called

I have completed all the needed code for delegate to work. In my viewcontroller:
class ViewController: UIViewControllerCustomViewDelegate
I also have this:
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
func goToNextScene() {
print("GOTOSCENE2")
}
And in my custom view I have this:
import UIKit
protocol CustomViewDelegate: class { // make this class protocol so you can create `weak` reference
func goToNextScene()
}
#available(iOS 10.0, *)
class ImageHeaderView : UIView {
#IBOutlet weak var followme: UISwitch!
#IBOutlet weak var profileImage : UIImageView!
#IBOutlet weak var backgroundImage : UIImageView!
weak var delegate: CustomViewDelegate?
override func awakeFromNib() {
super.awakeFromNib()
self.backgroundColor = UIColor(hex: "E0E0E0")
self.profileImage.layer.cornerRadius = self.profileImage.frame.size.height / 2
self.profileImage.clipsToBounds = true
self.profileImage.layer.borderWidth = 1
self.profileImage.layer.borderColor = UIColor.white.cgColor
//self.profileImage.setRandomDownloadImage(80, height: 80)
//self.backgroundImage.setRandomDownloadImage(Int(self.frame.size.width), height: 100)
}
#IBAction func followme(_ sender: AnyObject) {
UserDefaults.standard.set(followme.isOn, forKey: "followme")
}
#IBAction func logout(_ sender: AnyObject) {
delegate?.goToNextScene()
print("GOTOSCENE")
}
}
There is are no errors thrown but when I click/tap the button, nothing happens. It just prints "GOTOSCENE".
What I feel is
Your problem is right here
override func viewDidLoad() {
super.viewDidLoad()
let myCustomView = Bundle.main.loadNibNamed("ImageHeaderView", owner: self, options: nil)?[0] as! ImageHeaderView
myCustomView.delegate = self
}
I think you have added imageHeaderview from storyboard and in viewdidload you are creating new object of ImageHeaderView and assigning delegate to newly created object.
Try to outlet your ImageHeaderView and assign delegate to outleted object.
Hope this will fix your issue.

How to get reference to UIView in scene dock?

I have UIViewController with 2 UIViews (scenes) in dock:
How can i display it in whatever place I need in my UIViewController? In other words, how can I get reference to this views from within my controller?
Swift 3, Xcode 8
1. Create a Reference
You can drag the UIViews to your ViewController and create outlets for them.
2. To Show UIView
You want to add it to the view as a subview. I hooked up those two buttons you see in my scene:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
}
3. To Hide the UIViews
You will have to remove them from your view. I created actions for the Close buttons on the UIViews:
class ViewController: UIViewController {
#IBOutlet var view1: UIView!
#IBOutlet var view2: UIView!
// SHOW THE VIEWS
#IBAction func showView1(_ sender: UIButton) {
view.addSubview(view1)
view1.center = view.center
}
#IBAction func showView2(_ sender: UIButton) {
view.addSubview(view2)
view2.center = CGPoint(x: view.center.x, y: 100)
}
// HIDE THE VIEWS
#IBAction func hideView1(_ sender: UIButton) {
view1.removeFromSuperview()
}
#IBAction func hideView2(_ sender: UIButton) {
view2.removeFromSuperview()
}
}
Hope this helps!
Within my UIViewController's code keep references using #IBOutlet:
#IBOutlet weak var dockView: UIView!

Resources