How to show a backgroundVIew for empty tableView using RxSwift - swift

not very expert with RxSwift, I tested some solution found here, such this but not working. Don't know if issue is about size of an empty tableview or som other UI update related issue.
the tableView I am using is a bit customized such this
public class SelfSizedTableView: UITableView {
public override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
public override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
public override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
the solution I am testing
extension UITableView {
func setEmptyMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont(name: "TrebuchetMS", size: 15)
messageLabel.sizeToFit()
self.backgroundView = messageLabel
self.separatorStyle = .none
}
func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}
self.viewModel.things.subscribe(onNext: { [unowned self] things in
if things.isEmpty {
self.myTableView.backgroundColor = .purple
self.myTableView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
self.myTableView.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(200), height: CGFloat(200))
self.myTableView.setNeedsLayout()
self.myTableView.layoutIfNeeded()
self.myTableView.setEmptyMessage("My Message")
} else {
self.myTableView.restore()
}
}).disposed(by: self.rx.disposeBag)

I think you can use a pod called pod 'EmptyDataSet-Swift' It will help you do this regarding any empty tableView

Try this
Update UIElements inside DispatchQueue.main.async {...}
self.viewModel.things.subscribe(onNext: { [unowned self] things in
if things.isEmpty {
DispatchQueue.main.async {
self.myTableView.backgroundColor = .purple
self.myTableView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
self.myTableView.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(200), height: CGFloat(200))
self.myTableView.setNeedsLayout()
self.myTableView.layoutIfNeeded()
self.myTableView.setEmptyMessage("My Message")
}
} else {
DispatchQueue.main.async {
self.myTableView.restore()
}
}
}).disposed(by: self.rx.disposeBag)

Related

CustomAlert Message overWrite

my model :
struct Model : Codable {
let title : String
var target : Int
var read : Int
let mean : String
let useful : String
}
and I create custom alert messages model:
class MyAlert {
struct Constants {
static let backgroundAlphaTo : CGFloat = 0.6
}
private var backgroundView : UIView = {
let backgroundView = UIView()
backgroundView.backgroundColor = .black
backgroundView.alpha = 0
return backgroundView
}()
private let alertView : UIView = {
let alertView = UIView()
alertView.backgroundColor = .white
alertView.layer.masksToBounds = true
alertView.layer.cornerRadius = 12
return alertView
}()
private var myTargetView : UIView?
func showAlert(with title :String , message : String , on ViewController : UIViewController){
guard let targetView = ViewController.view else {
return
}
myTargetView = targetView
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
targetView.addSubview(alertView)
alertView.frame = CGRect(
x: 40, y: -300, width: targetView.frame.size.width-80, height: 300
)
let titleLabel = UILabel(frame: CGRect(
x: 0,
y: 0,
width: alertView.frame.size.width,
height: 80))
titleLabel.text = title
titleLabel.textAlignment = .center
alertView.addSubview(titleLabel)
let messageLabel = UILabel(frame: CGRect(
x: 0,
y: 80,
width: alertView.frame.size.width,
height: 170))
messageLabel.numberOfLines = 0
messageLabel.text = message
messageLabel.textAlignment = .center
alertView.addSubview(messageLabel)
let button = UIButton(frame: CGRect(
x: 0,
y: alertView.frame.size.height-50,
width: alertView.frame.size.width,
height: 50))
alertView.addSubview(button)
button.setTitle("Kapat", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(dissmissAlert), for: .touchUpInside)
UIView.animate(withDuration: 0.25) {
self.backgroundView.alpha = Constants.backgroundAlphaTo
} completion: { (done) in
if done {
UIView.animate(withDuration: 0.25) {
self.alertView.center = targetView.center
}
}
}
}
#objc func dissmissAlert() {
guard let targetView = myTargetView else {
return
}
UIView.animate(withDuration: 0.25, animations: {
self.alertView.frame = CGRect(
x: 40, y: targetView.frame.size.height, width: targetView.frame.size.width-80, height: 300
)}, completion: {done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.backgroundView.alpha = 0
}, completion: {done in
if done {
self.alertView.removeFromSuperview()
self.backgroundView.removeFromSuperview()
}
})
}
}
)
}
}
and I have segmentController :
#objc func ButtonTapped( _ sender : UISegmentedControl) {
if sender.selectedSegmentIndex == 1 {
customAlert.showAlert(with: zikirs.title, message: zikirs.mean, on: self)
} else if sender.selectedSegmentIndex == 2 {
customAlert.showAlert(with: zikirs.title, message: zikirs.useful, on: self)
}
}
private func dismissAlert(){
customAlert.dissmissAlert()
}
the problem here is the first message is normal but the second message overwrites the other.
how can I overcome this problem. I think this is from the inheritance property of the Classes. but I wanted to do my CustomAlert model with Struct because #objc can be only classes
The problem is that you don't remove the labels and the button from alertView but add new ones on the next call.
My suggestion is to assign tags to the views and create them only the first time
class MyAlert {
struct Constants {
static let backgroundAlphaTo : CGFloat = 0.6
}
private var backgroundView : UIView = {
let backgroundView = UIView()
backgroundView.backgroundColor = .black
backgroundView.alpha = 0
return backgroundView
}()
private let alertView : UIView = {
let alertView = UIView()
alertView.backgroundColor = .white
alertView.layer.masksToBounds = true
alertView.layer.cornerRadius = 12
return alertView
}()
private var myTargetView : UIView?
func showAlert(with title :String , message : String , on ViewController : UIViewController){
guard let targetView = ViewController.view else {
return
}
myTargetView = targetView
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
targetView.addSubview(alertView)
alertView.frame = CGRect(
x: 40, y: -300, width: targetView.frame.size.width-80, height: 300
)
if let titleLabel = alertView.viewWithTag(100) as? UILabel {
titleLabel.text = title
} else {
let titleLabel = UILabel(frame: CGRect(
x: 0,
y: 0,
width: alertView.frame.size.width,
height: 80))
titleLabel.tag = 100
titleLabel.text = title
titleLabel.textAlignment = .center
alertView.addSubview(titleLabel)
}
if let messageLabel = alertView.viewWithTag(101) as? UILabel {
messageLabel.text = message
} else {
let messageLabel = UILabel(frame: CGRect(
x: 0,
y: 80,
width: alertView.frame.size.width,
height: 170))
messageLabel.tag = 101
messageLabel.numberOfLines = 0
messageLabel.text = message
messageLabel.textAlignment = .center
alertView.addSubview(messageLabel)
}
if alertView.viewWithTag(102) == nil {
let button = UIButton(frame: CGRect(
x: 0,
y: alertView.frame.size.height-50,
width: alertView.frame.size.width,
height: 50))
button.tag = 102
alertView.addSubview(button)
button.setTitle("Kapat", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(dissmissAlert), for: .touchUpInside)
}
UIView.animate(withDuration: 0.25) {
self.backgroundView.alpha = Constants.backgroundAlphaTo
} completion: { (done) in
if done {
UIView.animate(withDuration: 0.25) {
self.alertView.center = targetView.center
}
}
}
}
#objc func dissmissAlert() {
guard let targetView = myTargetView else {
return
}
UIView.animate(withDuration: 0.25, animations: {
self.alertView.frame = CGRect(
x: 40, y: targetView.frame.size.height, width: targetView.frame.size.width-80, height: 300
)}, completion: {done in
if done {
UIView.animate(withDuration: 0.25, animations: {
self.backgroundView.alpha = 0
}, completion: {done in
if done {
self.alertView.removeFromSuperview()
self.backgroundView.removeFromSuperview()
}
})
}
}
)
}
}

How to update ScrollView Width after device rotation?

I am following a tutorial to create a swift app that adds a number of views to a scroll view and then allows me to scroll between them. I have the app working and understand it for the most part. When I change the orientation of the device the views width don't get updated so I have parts of more than one view controller on the screen? Does anybody know how to fix this? From my understanding I need to call something in the viewsDidTransition method to redraw the views. Any help would be greatly appreciated. Thanks!
Here is what I have so far:
import UIKit
class ViewController: UIViewController {
private let scrollView = UIScrollView()
private let pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.numberOfPages = 5
pageControl.backgroundColor = .systemBlue
return pageControl
}()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
pageControl.addTarget(self,
action: #selector(pageControlDidChange(_:)),
for: .valueChanged)
scrollView.backgroundColor = .red
view.addSubview(scrollView)
view.addSubview(pageControl)
}
#objc private func pageControlDidChange(_ sender: UIPageControl){
let current = sender.currentPage
scrollView.setContentOffset(CGPoint(x: CGFloat(current) * view.frame.size.width,
y: 0), animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
pageControl.frame = CGRect(x: 10, y: view.frame.size.height - 100, width: view.frame.size.width - 20, height: 70)
scrollView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height - 100)
if scrollView.subviews.count == 2 {
configureScrollView()
}
}
private func configureScrollView(){
scrollView.contentSize = CGSize(width: view.frame.size.width*5, height: scrollView.frame.size.height)
scrollView.isPagingEnabled = true
let colors: [UIColor] = [.systemRed, .systemGray, .systemGreen, .systemOrange, .systemPurple]
for x in 0..<5{
let page = UIView(frame: CGRect(x: CGFloat(x) * view.frame.size.width, y: 0, width: view.frame.size.width, height: scrollView.frame.size.height))
page.backgroundColor = colors[x]
scrollView.addSubview(page)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
print("hello world")
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
pageControl.currentPage = Int(floorf(Float(scrollView.contentOffset.x) / Float(scrollView.frame.size.width)))
}
}

UIScrollView with paging enable not working properly

Trying to add four UIView's in UIScrollView
The issue is next (2, 3, and 4) view gets a bit off from the visible screen view when scroll.
override init(frame: CGRect) {
super.init(frame: frame)
configureSlider()
}
private func configureSlider() {
Bundle.main.loadNibNamed("CarouselSliderView", owner: self, options: nil)
addSubview(contentView)
for i in 0...3 {
let view = UIView()
switch i {
case 0:
view.backgroundColor = .brown
case 1:
view.backgroundColor = .yellow
case 2:
view.backgroundColor = .green
case 3:
view.backgroundColor = .blue
default:
view.backgroundColor = .red
}
slides.append(view)
}
setupSlideScrollView(slides: slides)
pageControl.numberOfPages = slides.count
pageControl.currentPage = 0
self.bringSubviewToFront(pageControl)
}
set each UIView frames:
private func setupSlideScrollView(slides : [UIView]) {
let screenRect = UIScreen.main.bounds
let screenWidth = screenRect.size.width
for i in 0 ..< slides.count {
slides[i].frame = CGRect(x: screenWidth * CGFloat(i), y: 0, width: screenWidth, height: self.frame.height)
topScrollView.addSubview(slides[i])
}
topScrollView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: self.frame.height)
topScrollView.contentSize = CGSize(width: screenWidth * CGFloat(slides.count), height: self.frame.height)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x/self.frame.width)
pageControl.currentPage = Int(pageIndex)
}

How to add a Label for Each Image in the iCarousel View Framework

I have used the iCarousel Framework in my View Controller. But, I am not sure how can I add a label below each image while scrolling in the carousel view. Would appreciate your help! Thanks!
func numberOfItems(in carousel: iCarousel) -> Int {
return 10
}
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let view = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width/1.4, height: 300))
view.backgroundColor = .red
let imageview = UIImageView(frame: view.bounds)
view.addSubview(imageview)
imageview.contentMode = .scaleToFill
imageview.image = UIImage(named: "Dog_\(index+1)")
return view
}
func carouselDidEndScrollingAnimation() {
if (myCarousel.currentItemIndex == 1) {
dogNameLabel.text = "Brownie!"
}
}
let myCarousel: iCarousel = {
let view = iCarousel()
view.type = .rotary
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(myCarousel)
myCarousel.dataSource = self
myCarousel.frame = CGRect(x: 0, y: 220, width: view.frame.size.width, height: 400)
}
There are too many option. You can create custom view (xib) for reusability. You need to label in contentView above imageView.
But you need to create view like that.
func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
let view = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width/1.4, height: 300))
view.backgroundColor = .red
let imageview = UIImageView(frame: view.bounds)
view.addSubview(imageview)
imageview.contentMode = .scaleToFill
imageview.image = UIImage(named: "Dog_\(index+1)")
let label = UILabel()
label.text = "Enter your text"
label.frame = CGRect(x: 0, y: 20, width: view.frame.size.width, height: 30)
//or use constraints
view.addSubview(label)
return view
}

Swift access helper function view

I try to access the function "addNavBar()" from, my helper class, but when I run the emulator, no view is shown on HomeViewController.
Swift 4
HomeViewController.swift
class HomeController: UIViewController {
let NavBar = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
NavBar.addNavBar()
}
}
NavigationBarHelper.swift
class NavigationBarHelper: UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
self in NavigationBarHelper is not the same object as the view in the view controller. Pass the VC's view as a parameter. There is no need to make NavigationBarHelper a subclass of UIView (in fact it could also be a struct).
class NavigationBarHelper {
func addNavBar(to view: UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
please also stick to naming conventions
class HomeController: UIViewController {
let navBarHelper = NavigationBarHelper()
override func viewDidLoad() {
super.viewDidLoad()
navBarHelper.addNavBar(to: self.view)
}
Instead of creating an object every usage
let NavBar = NavigationBarHelper()
I think it's more robust to make it static like this
class NavigationBarHelper: UIView {
static func addNavBar(view:UIView) {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
view.addSubview(navBarView)
}
}
and call it directly without object creation
NavigationBarHelper.addNavBar(view: self.view)
A Better Alternative for this would be an extension so you don't have to create the special class for this
extension UIView {
func addNavBar() {
let rect = CGRect(x: 10, y: 70, width: 250, height: 100)
let navBarView = UIView(frame: rect)
navBarView.backgroundColor = UIColor.blue
self.addSubview(navBarView)
}
}
And in you UIViewController you can simply write this without creating an object of your helper.
self.view.addNavBar()