I would like to implement the factory design pattern in Swift 3.0.
The basic solution that I am thinking of is:
Define a protocol that define as object creation method
Implement the protocol within the Object
Is this a sound approach?
Or are there alternative design patterns in Swift?
You can try to use this implementation as a reference.
https://redflowerinc.com/implementing-factory-design-pattern-in-swift/
import UIKit
import PlaygroundSupport
enum Maps : Int {
case google = 1
case apple
}
protocol Display {
func showMap()
}
class Map {
let type : Int = 0
func showMap(type : Maps) -> Display {
switch type {
case Maps.apple :
return AppleMap()
case Maps.google :
fallthrough
default:
return GoogleMap()
}
}
}
class AppleMap : Display {
func showMap() {
print("showing apple map")
}
}
class GoogleMap : Display {
func showMap() {
print("showing google map")
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.text = "Hello World!"
label.textColor = .black
view.addSubview(label)
self.view = view
let map = Map()
map.showMap(type: Maps.google)
map.showMap(type: Maps.apple)
}
}
Related
I use UIView as alert view in my app, and i want to show it as banner on top of screen, when device is not connected to internet. So my issue that this view appears under my nav bar, how can i bring it to front ? I've tried to us UIApplication.shared.keyWindow! and add my backgroundView as subview to it, but it causes other issues.
This is my alert view class: I'll provide all class, but my realisation is in show() method.
import Foundation
import UIKit
import SnapKit
class ConnectionAlertView: UIView, UIGestureRecognizerDelegate {
internal var backgroundView: UIView = {
let view = UIView()
view.backgroundColor = Theme.Color.alertLabelBackgroundColor
view.alpha = 0
view.layer.cornerRadius = 15
return view
}()
internal var dismissButton: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "close_icon")
imageView.layer.cornerRadius = 15
imageView.isUserInteractionEnabled = true
return imageView
}()
internal var descriptionTitleLabel: UILabel = {
let label = UILabel()
label.text = "Відсутнє підключення до Інтернету"
label.font = Theme.Font.fontBodyLarge
label.textColor = .white
return label
}()
internal var descriptionLabel: UILabel = {
let label = UILabel()
label.text = "Перевірте налаштування мережі"
label.font = Theme.Font.fontBodyMedium
label.textColor = .white
return label
}()
// MARK: - Private Methods -
internal func layout() {
backgroundView.addSubview(descriptionTitleLabel)
backgroundView.addSubview(descriptionLabel)
backgroundView.addSubview(dismissButton)
descriptionTitleLabel.snp.makeConstraints { make in
make.trailing.equalTo(backgroundView).offset(54)
make.leading.equalTo(backgroundView).offset(16)
make.top.equalTo(backgroundView).offset(12)
}
descriptionLabel.snp.makeConstraints { make in
make.leading.equalTo(descriptionTitleLabel.snp.leading)
make.top.equalTo(descriptionTitleLabel.snp.bottom).offset(4)
}
dismissButton.snp.makeConstraints { make in
make.width.height.equalTo(30)
make.centerY.equalTo(backgroundView)
make.trailing.equalTo(backgroundView).offset(-16)
}
}
internal func configure() {
let tap = UITapGestureRecognizer(target: self, action: #selector(dismiss(sender:)))
tap.delegate = self
dismissButton.addGestureRecognizer(tap)
}
// MARK: - Public Methods -
func show(viewController: UIViewController) {
guard let targetView = viewController.view else { return }
backgroundView.frame = CGRect(x: 10, y: 50, width: targetView.frame.width - 20 , height: 67)
targetView.addSubview(targetView)
layout()
configure()
// show view
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
UIView.transition(with: self.backgroundView, duration: 0.6,
options: .transitionCrossDissolve,
animations: {
self.backgroundView.alpha = 1
})
}
// hide view after 5 sec delay
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
UIView.transition(with: self.backgroundView, duration: 1,
options: .transitionCrossDissolve,
animations: {
self.backgroundView.alpha = 0
})
}
}
// MARK: - Objc Methods -
#objc internal func dismiss(sender: UITapGestureRecognizer) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
UIView.transition(with: self.backgroundView, duration: 1,
options: .transitionCrossDissolve,
animations: {
self.backgroundView.alpha = 0
})
}
}
}
My viewController:
class PhoneNumViewController: UIViewController {
let alert = ConnectionAlertView()
private func checkInternetConnection() {
if !NetworkingMonitor.isConnectedToInternet {
log.error("No internet connection!")
alert.show(viewController: self)
}
}
}
Since you have a navigation controller and do not wish to add this view to the window directly, I can offer the following idea which could work.
Your UIViewController is contained with the UINavigationController so if you add the alert to your UIViewController, you will notice it below the UINavigationBar.
You could instead show the alert from your UINavigationController instead with the following changes.
1.
In the func show(viewController: UIViewController) in your class ConnectionAlertView: UIView I changed the following line:
targetView.addSubview(targetView)
to
targetView.addSubview(backgroundView)
This does not directly relate to your issue but seems to be a bug and causes a crash as it seems like you want to add the background view on the target view.
2.
In your class ViewController: UIViewController, when you want to show your alert view, pass the UINavigationController instead like this:
if let navigationController = self.navigationController
{
alert.show(viewController: navigationController)
}
This should give you the desired result I believe (The image and font looks different as I do not have these files but should work fine at your end):
I am currently trying to implement a cocoa pod, but when using it I get the error "Cannot call value of non-function type 'SwipeCard'" in the function " func cardStack". It may a silly mistake since I am just learning how to code in swift.
import UIKit
import Shuffle
class CardsViewViewController: UIViewController, SwipeCardStackDataSource {
let card = SwipeCard()
let cardStack = SwipeCardStack()
let cardImages = [
UIImage(named: "cardImage1"),
UIImage(named: "cardImage2"),
UIImage(named: "cardImage3")
]
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(cardStack)
cardStack.frame = view.safeAreaLayoutGuide.layoutFrame
cardStack.dataSource = self
}
func cardStack(_ cardStack: SwipeCardStack, cardForIndexAt index: Int) -> SwipeCard {
return card(fromImage: cardImages[index])
}
func numberOfCards(in cardStack: SwipeCardStack) -> Int {
return cardImages.count
}
}
There is a function included in the package as well, but according to the installation guide, this is what I am supposed to do (but I opted to just implement card = SwipeCard() instead of putting the whole function):
1 -Create your own card by either subclassing SwipeCard or setting its properties directly:
func card(fromImage image: UIImage) -> SwipeCard {
let card = SwipeCard()
card.swipeDirections = [.left, .right]
card.content = UIImageView(image: image)
let leftOverlay = UIView()
leftoverlay.backgroundColor = .green
let rightOverlay = UIView()
rightOverlay.backgroundColor = .red
card.setOverlays([.left: leftOverlay, .right: rightOverlay])
return card
}
2 - The card returned from card(fromImage:) displays an image, can be swiped left or right, and has overlay views for both directions.
What am I getting wrong?
Thank you so much!
you have to remove let card = SwipeCard() and leaving just the following function:
func card(fromImage image: UIImage) -> SwipeCard {
let card = SwipeCard()
card.swipeDirections = [.left, .right]
card.content = UIImageView(image: image)
let leftOverlay = UIView()
leftoverlay.backgroundColor = .green
let rightOverlay = UIView()
rightOverlay.backgroundColor = .red
card.setOverlays([.left: leftOverlay, .right: rightOverlay])
return card
}
which is basically the one you call inside
func cardStack(_ cardStack: SwipeCardStack, cardForIndexAt index: Int) -> SwipeCard
The error is telling you that you are trying to call a SwipeCard instance instead of a method.
I think you are referring to the constant let card rather than the function inside the above function.
I'm trying to create a custom alertview with dynamic behaviour using UIDynamicAnimator in NSObject class using swift,While adding UISnapBehaviour to a view in NSObject class init method snap behaviour is not working,For instance look at the below code
import UIKit
class DynamicBehaviour: NSObject {
var Animator:UIDynamicAnimator!
var TargetView:UIView!
var TestView:UIView!
override init() {
super.init()
}
init(SourceViews:UIView) {
super.init()
TestView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
TestView.backgroundColor = UIColor.blueColor()
TargetView.addSubview(TestView)
Animator = UIDynamicAnimator(referenceView: TargetView)
let snapBehavior: UISnapBehavior = UISnapBehavior(item: TestView, snapToPoint: TargetView.center)
Animator.addBehavior(snapBehavior)
}
}
"TestView" is added as subview to "Target" but snap behaviour is not working.
I tried the same code in ObjectiveC
#import "DynamicBehaviour.h"
#import <UIKit/UIKit.h>
#interface DynamicBehaviour ()
#property(nonatomic,strong)UISnapBehavior * Snap_behaviour;
#property (nonatomic,strong)UIDynamicAnimator * Animator;
#end
#implementation DynamicBehaviour
-(instancetype)initWithSourceView:(UIView *)TargetView{
if (self = [super init]) {
UIView * TestView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
TestView.backgroundColor = [UIColor blueColor];
[TargetView addSubview:TestView];
self.Animator = [[UIDynamicAnimator alloc]initWithReferenceView:TargetView];
self.Snap_behaviour = [[UISnapBehavior alloc]initWithItem:TestView snapToPoint:TargetView.center];
[self.Animator addBehavior:self.Snap_behaviour];
}
return self;
}
#end
it works fine,the "TestView" snaps to the centre of TargetView.I don't know whats wrong with swift code.
Here's what I've tried:
The dynamic effect is working fine when coded in UIViewController class,problem exist only while subclassing NSObject in swift.
I have tried the other dynamic behaviours such as UIGravityBehavior the same problem exists in swift.
Done the same sample work in ObjectiveC object class its working fine,I've attached the working ObjC code too.
It seems that the problem may exist in init method or variable decleration am not sure about that
However, I don't know how to fix that problem. I've read through many articles on the internet. I've also searched StackOverflow. Please help.
Your code in Swift is not exactly the same as Objective-C code: Objective-C code has strong reference Snap_behaviour, but Swift code has only local readonly variable.
Here is the Swift equivalent:
class DynamicBehaviour: NSObject {
var Animator:UIDynamicAnimator!
var snapBehaviour:UISnapBehavior!
var TargetView:UIView!
var TestView:UIView!
override init() {
super.init()
}
init(SourceViews:UIView) {
super.init()
TestView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
TestView.backgroundColor = UIColor.blueColor()
TargetView.addSubview(TestView)
Animator = UIDynamicAnimator(referenceView: TargetView)
snapBehaviour = UISnapBehavior(item: TestView, snapToPoint: TargetView.center)
Animator.addBehavior(snapBehaviour)
}
}
Usually I use UIDynamicBehaviour as base class for combined behaviors like this
class CombinedBehavior: UIDynamicBehavior {
lazy var collider: UICollisionBehavior = {
let lazilyCreatedCollider = UICollisionBehavior()
lazilyCreatedCollider.translatesReferenceBoundsIntoBoundary = true
lazilyCreatedCollider.collisionMode = UICollisionBehaviorMode.Everything
return lazilyCreatedCollider
}()
lazy var itemBehavior: UIDynamicItemBehavior = {
let lazilyCreatedBehavior = UIDynamicItemBehavior()
lazilyCreatedBehavior.allowsRotation = true
lazilyCreatedBehavior.elasticity = 0
lazilyCreatedBehavior.resistance = 100
lazilyCreatedBehavior.angularResistance = 100
lazilyCreatedBehavior.friction = 100
return lazilyCreatedBehavior
}()
override init() {
super.init()
addChildBehavior(collider)
addChildBehavior(itemBehavior)
}
func addBarrier(path: UIBezierPath, named name: String) {
collider.removeBoundaryWithIdentifier(name)
collider.addBoundaryWithIdentifier(name, forPath: path)
}
func addView(view: UIView) {
dynamicAnimator?.referenceView?.addSubview(view)
collider.addItem(view)
itemBehavior.addItem(view)
}
func removeView(view: UIView) {
collider.removeItem(view)
itemBehavior.removeItem(view)
view.removeFromSuperview()
}
}
Put this code in your controller
lazy var animator: UIDynamicAnimator = {
let lazilyCreatedDynamicAnimator = UIDynamicAnimator(referenceView: self.view) // or any view you needed
// if you need delegate in your controller uncomment this
// lazilyCreatedDynamicAnimator.delegate = self
return lazilyCreatedDynamicAnimator
}()
var combinedBehavior = CombinedBehavior()
override func viewDidLoad() {
// your other code
animator.addBehavior(combinedBehavior)
}
I have a Visual Effect View on storyboard connected to my ViewController as an outlet. The effect is burring an ImageView behind it and works great. I'm trying to change the UIBlurEffectStyle from Light to Dark inside a button click IBAction. Any help here would be much appreciated!
#IBOutlet weak var blurView: UIVisualEffectView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func changeBlurView() {
// This is wrong, but is my best attempt so far.
self.blurView(UIBlurEffectStyle.Dark)
}
While creating my own app I faced to a similar problem. I do not use IB at all, so everything is done programatically. I looked into the UIVisualEffectView.h and it does not provide any effect change on the fly (hopefully this will change in the future).
So here is my solution (I'm using the latest Swift version, so there is an as! operator):
class CustomVisualEffectView : UIVisualEffectView
{
deinit
{
println("UIVisualEffectView will be destroyed.")
}
}
class ViewController: UIViewController
{
var _blurEffect = UIBlurEffect() // global so you can use it for vibrancy effect view as well
var _blurredEffectView = CustomVisualEffectView()
let _someSubView = UIView()
let _someOtherSubView = UIView()
let _effectChanger = UIButton.buttonWithType(.System) as! UIButton
override func viewDidLoad()
{
super.viewDidLoad()
self.view.backgroundColor = UIColor.orangeColor()
/* create a button to change the effect */
_effectChanger.setTitle("Change effect!", forState: UIControlState.Normal)
_effectChanger.frame.size = CGSize(width: 100, height: 20)
_effectChanger.center = self.view.center
_effectChanger.addTarget(self, action: Selector("buttonClicked"), forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(_effectChanger)
/* here is our effect view */
_blurEffect = UIBlurEffect(style: self.randomBlurEfffectStyle())
_blurredEffectView = CustomVisualEffectView(effect: _blurEffect)
self.layoutEffectView()
self.view.addSubview(_blurredEffectView)
/* create two subviews and put them on the effect view */
_someSubView.frame = CGRectMake(10, 10, 10, 10)
_someSubView.backgroundColor = UIColor.redColor()
_blurredEffectView.contentView.addSubview(_someSubView)
_someOtherSubView.frame = CGRectMake(30, 30, 10, 10)
_someOtherSubView.backgroundColor = UIColor.greenColor()
_blurredEffectView.contentView.addSubview(_someOtherSubView)
}
func layoutEffectView()
{
_blurredEffectView.frame.size = CGSize(width: 100, height: 80)
_blurredEffectView.center = CGPointMake(_effectChanger.center.x, _effectChanger.center.y - 50)
}
func buttonClicked()
{
var tempArray = [AnyObject]()
/* get all subviews from the effect view */
for view in _blurredEffectView.contentView.subviews
{
tempArray.append(view)
view.removeFromSuperview()
}
/* modify your effect view */
_blurEffect = UIBlurEffect(style: self.randomBlurEfffectStyle())
/* IMPORTANT: so the old effect view can be destroyed by arc */
_blurredEffectView.removeFromSuperview()
_blurredEffectView = CustomVisualEffectView(effect: _blurEffect)
/* I think this will be really tricky if you will use auto layout */
self.layoutEffectView()
self.view.addSubview(_blurredEffectView)
/* put all subviews back to the effect view*/
for view in tempArray
{
_blurredEffectView.contentView.addSubview(view as! UIView)
}
}
func randomBlurEfffectStyle() -> UIBlurEffectStyle
{
let randomBlurEffectStyle : UIBlurEffectStyle
switch Int(arc4random_uniform(3)) // [0,1,2]
{
case 0:
randomBlurEffectStyle = .ExtraLight
case 1:
randomBlurEffectStyle = .Light
default:
randomBlurEffectStyle = .Dark
}
return randomBlurEffectStyle
}
}
I am trying to create a bar graph class in swift but having difficulty setting up a delegate....heres what I have so far...
BarChart.swift:
import Foundation
import UIKit
#objc protocol BarChartDelegate {
optional func barChart(colorForBarAtIndex index: Int) -> UIColor
}
class BarChart : BarChartDelegate {
let data : [NSDecimalNumber]
let container = UIView()
var barWidth : CGFloat = 100
var barSpacing : CGFloat = 10
var delegate : BarChartDelegate?
init(data: [NSDecimalNumber], frame: CGRect) {
self.data = data
self.container.frame = frame
drawGraph()
}
func drawGraph() {
var i = 0
for item in self.data {
var bar = UIView()
let xPos = CGFloat(i)*self.barWidth
bar.frame = CGRectMake(xPos, 0, self.barWidth, 100)
if let del = delegate? {
bar.frame
println(del.barChart!(colorForBarAtIndex: i))
bar.backgroundColor = del.barChart!(colorForBarAtIndex: i)
}
else {
println("nope!")
}
self.container.addSubview(bar)
i++
}
}
}
ViewController.swift
class ViewController: UIViewController, BarChartDelegate {
var colors = [
UIColor.greenColor(),
UIColor.blueColor(),
UIColor.redColor()
]
override func viewDidLoad() {
super.viewDidLoad()
var barChart = BarChart(data: [NSDecimalNumber(double: 100.00), NSDecimalNumber(double: 200.00), NSDecimalNumber(double: 300.00)], frame: CGRectMake(0, 0, 400.00, 100.00))
self.view.addSubview(barChart.container)
}
func barChart(colorForBarAtIndex index: Int) -> UIColor { // this is not running?
return self.colors[index]
}
}
the delegate method in my ViewController.swift file is not being run and I just get "nope!" printed to the console 3 times... which is a result of the delegate optional finding nil when unwrapping?
Is there something I am missing here?
Any help would be appreciated!
Thanks,
Dave
First off, it rarely makes sense for a BarChart to also be a BarChartDelegate. You don't delegate things to yourself!
Second, as far as I can tell you're not actually setting the ViewController as the delegate of the BarChart. Simply adopting the BarChartDelegate protocol is not enough to do that; you need to set it explicitly.
So, for example, you might want to do this after you create the BarChart:
var barChart = ...
barChart.delegate = self
Alternatively, if the delegate is vital for your chart, you might want to change the constructor to accept a delegate as an argument.