I'm creating a ViewPager in Swift using ViewCode.
I need to create the following action: tap on the Next button (method: actionNextPressed() ), and scroll the scrollview to the next page (like a TapGesture scrolling to right or left).
Here is my Swift code, what I've tried until now, and the printscreen from the viewpager (at the moment)
PagerViewController.swift
import UIKit
import SnapKit
open class PagerViewController: UIViewController, UIPageViewControllerDelegate {
private lazy var dimmedView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = maxDimmedAlpha
return view
}()
private lazy var containerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 1, alpha: 0)
return view
}()
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.showsHorizontalScrollIndicator = false
scrollView.isPagingEnabled = true
scrollView.contentSize = CGSize(width: view.frame.width * CGFloat(pages.count), height: containerView.frame.height)
for i in 0..<pages.count {
var page = pages[i]
let dialog = PageViewController(
icon: page.icon,
titleText: page.title,
descriptionText: page.description,
titleActionButton: page.titleButton,
actionButton: page.actionButton
)!
scrollView.addSubview(dialog.view)
scrollView.subviews[i].frame = CGRect(
x: view.frame.width * CGFloat(i),
y: 0,
width: containerView.frame.width,
height: containerView.frame.height
)
}
scrollView.delegate = self
return scrollView
}()
lazy var pageControl: UIPageControl = {
let pageControl = UIPageControl()
pageControl.currentPage = 0
pageControl.numberOfPages = pages.count
pageControl.addTarget(self, action: #selector(pageControlTapHandler(sender:)), for: .allEvents)
pageControl.isUserInteractionEnabled = false
pageControl.pageIndicatorTintColor = .systemGray
pageControl.currentPageIndicatorTintColor = .systemBlue
pageControl.backgroundColor = .white
return pageControl
}()
private lazy var directionsButtonsStackView: UIStackView = {
let view = UIStackView()
view.axis = .horizontal
view.distribution = .fillEqually
view.backgroundColor = .red
return view
}()
private lazy var buttonJump = UIButton()
private lazy var buttonBefore = UIButton()
private lazy var buttonNext = UIButton()
private var titleJumpButton: String! = ""
private let maxDimmedAlpha: CGFloat = 0.6
open var pages: Array<PageModel>!
private var currentPage: Int = 0
init?(listPages: Array<PageModel>, titleJumpButton: String) {
super.init(nibName: nil, bundle: nil)
self.pages = listPages
self.titleJumpButton = titleJumpButton
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func pageControlTapHandler(sender: UIPageControl) {
scrollView.scrollTo(horizontalPage: sender.currentPage)
}
open override func viewDidLoad() {
super.viewDidLoad()
setupView()
addViewComponents()
setupConstraints()
}
private func setupView() {
self.buttonJump = PageButton(frame: .zero).build(
context: self,
title: titleJumpButton!,
selector: #selector(actionJumpPressed)
)
self.buttonBefore = PageButton(frame: .zero).build(
context: self,
title: "Before",
selector: #selector(actionBeforePressed)
)
self.buttonBefore.backgroundColor = .white
self.buttonNext = PageButton(frame: .zero).build(
context: self,
title: "Next",
selector: #selector(actionNextPressed)
)
self.buttonJump.setTitle(titleJumpButton, for: .normal)
}
private func addViewComponents() {
view.addSubview(dimmedView)
containerView.addSubview(scrollView)
containerView.addSubview(pageControl)
directionsButtonsStackView.addArrangedSubview(buttonBefore)
directionsButtonsStackView.addArrangedSubview(buttonNext)
containerView.addSubview(directionsButtonsStackView)
containerView.addSubview(buttonJump)
view.addSubview(containerView)
}
private func setupConstraints() {
dimmedView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.bottom.equalToSuperview()
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
}
containerView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(100)
make.bottom.equalToSuperview().inset(100)
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
make.margins.equalTo(20)
}
scrollView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.bottom.equalTo(pageControl.snp.top)
make.leading.equalTo(containerView.snp.leading)
make.trailing.equalTo(containerView.snp.trailing)
}
pageControl.snp.makeConstraints { make in
make.bottom.equalTo(directionsButtonsStackView.snp.top)
make.leading.equalTo(containerView.snp.leading).offset(20)
make.trailing.equalTo(containerView.snp.trailing).inset(20)
make.height.equalTo(30)
make.centerX.equalToSuperview()
}
directionsButtonsStackView.snp.makeConstraints { make in
make.bottom.equalTo(buttonJump.snp.top)
make.leading.equalTo(containerView.snp.leading).offset(20)
make.trailing.equalTo(containerView.snp.trailing).inset(20)
make.height.equalTo(60)
make.width.greaterThanOrEqualTo(0)
make.centerX.equalToSuperview()
}
buttonJump.snp.makeConstraints { make in
make.bottom.equalToSuperview().inset(10)
make.leading.equalTo(containerView).offset(20)
make.trailing.equalTo(containerView).inset(20)
make.centerX.equalToSuperview()
make.width.greaterThanOrEqualTo(0)
make.height.equalTo(50)
}
}
#objc private func actionJumpPressed() {
self.dismiss(animated: true, completion: nil)
}
#objc private func actionBeforePressed() {
if currentPage == 0 {
return
} else {
currentPage -= 1
pageControl.currentPage = currentPage
}
}
#objc private func actionNextPressed() {
if currentPage == pages.count {
return
} else {
currentPage += 1
pageControl.currentPage = currentPage
}
}
}
extension PagerViewController: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = Int(round(scrollView.contentOffset.x / view.frame.width))
pageControl.currentPage = pageIndex
currentPage = pageIndex
}
}
extension UIScrollView {
func scrollTo(horizontalPage: Int? = 0) {
var frame: CGRect = self.frame
frame.origin.x = frame.size.width * CGFloat(horizontalPage ?? 0)
self.scrollRectToVisible(frame, animated: true)
}
}
I had the same issue once with a collectionView. You have to turn off the .isPagingEnabled, scroll the scrollview where you want and turn it back on the paging.
func scrollTo(horizontalPage: Int? = 0) {
scrollView.isPagingEnabled = false
var frame: CGRect = self.frame
frame.origin.x = frame.size.width * CGFloat(horizontalPage ?? 0)
self.scrollRectToVisible(frame, animated: true)
scrollView.isPagingEnabled = true
}
Related
Encountered a problem: largeTitle of navigation bar collapsing to small title ~once per 20 attempts after refreshControl.endRefreshing().
tableView is first subview of subviews hierarchy, and top constraint is to superview (not to safeArea)
refreshControl added to tableView.refreshControl, not as subview of tableView
was trying call navigationBar.sizeToFit() after endRefreshing - not success.
Controller code:
override public func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
title = L10n.Trainers.trainers.uppercased()
navigationController?.navigationBar.layoutMargins = .init(top: 0, left: 16, bottom: 0, right: 16)
configureSubviews()
makeConstraints()
configureBindings()
configureActions()
setupTableView()
setupResultsTableView()
}
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
navigationController?.navigationBar.sizeToFit()
}
override public func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
navigationController?.navigationItem.largeTitleDisplayMode = .automatic
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
vm.onViewDidAppear()
mainView.tableView.refreshControl = mainView.refreshControl
}
override public func loadView() {
view = mainView
}
Refresh-logic code:
vm.isLoading.drive { [weak self] loading in
guard let self = self else { return }
if self.mainView.refreshControl.isRefreshing {
if !loading {
self.mainView.tableView.refreshControl?.endRefreshing()
}
} else {
if loading {
var style = LoaderParameters.blue
style.installConstraints = false
self.mainView.tableView.startLoading(with: style)
self.mainView.tableView.existedLoaderView?.centerX().centerY(-40)
} else {
self.mainView.tableView.stopLoadingProgress()
}
}
}.store(in: &subscriptions)
View code:
final class TrainersView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
configureSubviews()
makeConstraints()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private(set) lazy var refreshControl = UIRefreshControl().configureWithAutoLayout {
$0.tintColor = Asset.mainGrey.color
}
private(set) lazy var topView: UIView = {
let view = UIView()
view.backgroundColor = .clear
return view
}()
private(set) lazy var tableView = UITableView().configureWithAutoLayout {
$0.register(TrainerCell.self)
$0.tableFooterView = UIView()
$0.showsVerticalScrollIndicator = false
$0.separatorStyle = .none
$0.backgroundColor = Asset.mainWhite.color
}
private func configureSubviews() {
addSubview(tableView)
topView.addSubview(segmentedControl)
tableView.tableHeaderView = topView
tableView.tableHeaderView?.frame.size.height = 60
tableView.tableFooterView?.frame.size.height = 40
tableView.reloadData()
}
private func makeConstraints() {
tableView.pinToSuperview()
segmentedControl.centerX()
segmentedControl.bottomAnchor.constraint(equalTo: topView.bottomAnchor, constant: -8).priority(999).activate()
segmentedControl.widthAnchor ~ widthAnchor - 16 * 2
}
}
Who can help me? I create chat controller via UITableViewController, UINavigationController and I use InputAccessoryView. If I swipe screen to left (dismiss this controller) and return swipe to right (cancel dismiss) - table set adjustContentInset bottom to zero and InputAcessoryView close bottom content tableView. This problem created in ViewWillAppear event. My code:
UITableViewController:
// MARK: - Controller data
lazy var inputContainerView = ChatAccessoryView(frame: .zero, buttonSelector: #selector(sendMessage(sender:)), controller: self)
public var ticketID: Int = 0
private var lastMessageID: Int = 0
private var tableSections: [String] = []
private var tableRows: [[SupportTicketMessage]] = []
private var sendingMessage: Bool = false
private var URLTaskGetMessages: URLSessionDataTask?
private var URLTaskSendMessage: URLSessionDataTask?
// MARK: - Controller overrides
override func loadView() {
super.loadView()
tableView.register(ChatHeaderView.self, forHeaderFooterViewReuseIdentifier: "chatHeader")
tableView.register(ChatFromMessageTableViewCell.self, forCellReuseIdentifier: "chatFromMessage")
tableView.register(ChatToMessageTableViewCell.self, forCellReuseIdentifier: "chatToMessage")
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
if let messages = supportClass.getTicketMessages(ticketID) {
tableSections = messages.sections
tableRows = messages.rows
lastMessageID = messages.lastMessage
scrollTableToBottom(false)
} else {
tableView.setLoaderBackground("Загрузка сообщений...")
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
loadMessages()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
URLTaskGetMessages?.cancel()
URLTaskSendMessage?.cancel()
}
override var inputAccessoryView: UIView? {
return inputContainerView
}
override var canBecomeFirstResponder: Bool {
return true
}
ChatAccessoryView:
class ChatAccessoryView: UIView {
// MARK: - Get params to init
let buttonSelector: Selector
let controller: UIViewController
// MARK: - Data
private let textView = ChatTextView()
private let sendButton = LoadingButton(frame: .zero, text: "Отпр.")
private let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight))
// MARK: - Init
required init(frame: CGRect, buttonSelector: Selector, controller: UIViewController) {
self.buttonSelector = buttonSelector
self.controller = controller
super.init(frame: frame)
configureContents()
blurEffectConfigure()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Overrides
override var intrinsicContentSize: CGSize {
return .zero
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
blurEffectConfigure()
}
override func didMoveToWindow() {
super.didMoveToWindow()
if let window = window {
textView.bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
}
}
// MARK: - Private methods
private func configureContents() {
backgroundColor = .clear
autoresizingMask = .flexibleHeight
textView.placeholder = "Напишите сообщение..."
textView.maxHeight = 160
textView.font = .systemFont(ofSize: 17)
textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
textView.layer.cornerRadius = 8
textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 4)
sendButton.titleLabel?.font = .boldSystemFont(ofSize: 17)
sendButton.setTitleColor(controller.view.tintColor, for: .normal)
sendButton.addTarget(controller, action: buttonSelector, for: .touchUpInside)
blurView.translatesAutoresizingMaskIntoConstraints = false
textView.translatesAutoresizingMaskIntoConstraints = false
sendButton.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(blurView)
self.addSubview(textView)
self.addSubview(sendButton)
blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
blurView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
textView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
textView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
textView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8 ).isActive = true
textView.trailingAnchor.constraint(equalTo: sendButton.leadingAnchor, constant: -20).isActive = true
sendButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
sendButton.widthAnchor.constraint(equalToConstant: 48).isActive = true
sendButton.centerYAnchor.constraint(equalTo: textView.centerYAnchor).isActive = true
}
private func blurEffectConfigure() {
if #available(iOS 13.0, *) {
if traitCollection.userInterfaceStyle == .light {
blurView.effect = UIBlurEffect(style: .extraLight)
} else {
blurView.effect = UIBlurEffect(style: .dark)
}
}
}
// MARK: - Public methods
public func successSend() {
textView.text = ""
sendButton.loadingMode(false)
}
}
ChatTextView:
class ChatTextView: UITextView {
// MARK: - Data
var maxHeight: CGFloat = 0.0
public let placeholderTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = .clear
textView.isScrollEnabled = false
textView.isUserInteractionEnabled = false
textView.textColor = UIColor.black.withAlphaComponent(0.33)
return textView
}()
var placeholder: String? {
get {
return placeholderTextView.text
}
set {
placeholderTextView.text = newValue
}
}
// MARK: - Init
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
backgroundColor = UIColor.black.withAlphaComponent(0.06)
isScrollEnabled = false
autoresizingMask = [.flexibleWidth, .flexibleHeight]
NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: UITextView.textDidChangeNotification, object: self)
placeholderTextView.font = font
addSubview(placeholderTextView)
NSLayoutConstraint.activate([
placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor),
placeholderTextView.topAnchor.constraint(equalTo: topAnchor),
placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
colorThemeConfigure()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Overrides
override var text: String! {
didSet {
invalidateIntrinsicContentSize()
placeholderTextView.isHidden = !text.isEmpty
}
}
override var font: UIFont? {
didSet {
placeholderTextView.font = font
invalidateIntrinsicContentSize()
}
}
override var contentInset: UIEdgeInsets {
didSet {
placeholderTextView.contentInset = contentInset
}
}
override var intrinsicContentSize: CGSize {
var size = super.intrinsicContentSize
if size.height == UIView.noIntrinsicMetric {
layoutManager.glyphRange(for: textContainer)
size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom
}
if maxHeight > 0.0 && size.height > maxHeight {
size.height = maxHeight
if !isScrollEnabled {
isScrollEnabled = true
}
} else if isScrollEnabled {
isScrollEnabled = false
}
return size
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
colorThemeConfigure()
}
// MARK: - Private methods
private func colorThemeConfigure() {
if #available(iOS 13.0, *) {
if traitCollection.userInterfaceStyle == .light {
backgroundColor = UIColor.black.withAlphaComponent(0.06)
placeholderTextView.textColor = UIColor.black.withAlphaComponent(0.33)
} else {
backgroundColor = UIColor.white.withAlphaComponent(0.08)
placeholderTextView.textColor = UIColor.white.withAlphaComponent(0.33)
}
}
}
// MARK: - Obj C methods
#objc private func textDidChange(_ note: Notification) {
invalidateIntrinsicContentSize()
placeholderTextView.isHidden = !text.isEmpty
}
}
Screenshots error:
TableView loaded (all right)
Swipe screen to left and cancel this action (swipe right)
When I returned to controller I see this Bug (last message go to bottom)
Thanks!
You could try something like this:
UITableViewController:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateTableViewInsets()
}
private func updateTableViewInsets() {
let offSet = inputContainerView.frame.height
tableView?.contentInset.bottom = offSet
tableView?.scrollIndicatorInsets.bottom = offSet
}
Thank you all for your attention and trying to help me! I solved the problem as follows:
tableView.contentInsetAdjustmentBehavior = .never
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 53, right: 0)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
I disable automatic tableView.contentInsetAdjustmentBehavior and use tableView.conentInset. On open/close keyboard I change tableView.contentInset.
This works well for me, and pretty simple solution:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
reloadInputViews()
prepareTableViewForViewPresented()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
prepareTableViewForViewDismissed()
}
private func prepareTableViewForViewPresented() {
self.tableView.contentInset.bottom = 0
}
private func prepareTableViewForViewDismissed() {
self.tableView.contentInset.bottom = view.safeAreaInsets.bottom + (inputAccessoryView?.frame.height ?? 0)
}
Known issues:
- when keyboard is shown, tableview still goes behind keyboard
I have SegmentedControl with 2 lines using:
// AppDelegate
UILabel.appearanceWhenContainedInInstancesOfClasses([UISegmentedControl.self]).numberOfLines = 0
The problem is the line fonts are the same exact size. I need to change the titleTextAttributes for each line so that the second line is smaller then the first line.
I know I can use this for both lines:
segmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17))
How can I do this?
// The SegmentedControl
let segmentedControl: UISegmentedControl = {
let segmentedControl = UISegmentedControl(items: ["Pizza\n123.1K", "Turkey Burgers\n456.2M", "Gingerale\n789.3B"])
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
segmentedControl.tintColor = UIColor.orange
segmentedControl.backgroundColor = .white
segmentedControl.isHighlighted = true
segmentedControl.addTarget(self, action: #selector(selectedIndex(_:)), for: .valueChanged)
return segmentedControl
}()
You'll want to create a custom control by subclassing UIControl. Here's a quick example:
CustomSegmentedControl.swift
import UIKit
import CoreImage
public class CustomSegmentedControl: UIControl {
public var borderWidth: CGFloat = 1.0
public var selectedSegementIndex = 0 {
didSet {
self.styleButtons()
}
}
public var numberOfSegments: Int {
return self.segments.count
}
private var buttons: [UIButton] = []
private var stackView = UIStackView(frame: CGRect.zero)
private var stackBackground = UIView(frame: CGRect.zero)
private var segments: [NSAttributedString] = [] {
didSet {
for subview in self.stackView.arrangedSubviews {
subview.removeFromSuperview()
}
self.buttons = []
for i in 0..<segments.count {
let segment = segments[i]
self.createAndAddSegmentButton(title: segment)
}
self.styleButtons()
}
}
override public init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
private func setup() {
self.addSubview(stackBackground)
self.stackBackground.constrainToBounds(of: self)
self.addSubview(stackView)
self.stackView.constrainToBounds(of: self)
self.stackView.axis = .horizontal
self.stackView.distribution = .fillEqually
self.stackView.spacing = borderWidth
self.layer.cornerRadius = 5.0
self.layer.borderWidth = borderWidth
self.clipsToBounds = true
self.stackBackground.backgroundColor = tintColor
}
private func createAndAddSegmentButton(title: NSAttributedString) {
let button = createSegmentButton(title: title)
self.buttons.append(button)
self.stackView.addArrangedSubview(button)
}
private func createSegmentButton(title: NSAttributedString) -> UIButton {
let button = UIButton(frame: CGRect.zero)
button.titleLabel?.numberOfLines = 0
button.titleLabel?.textAlignment = .center
button.setAttributedTitle(title, for: .normal)
button.addTarget(self, action: #selector(self.actSelected(button:)), for: .touchUpInside)
return button
}
override public var tintColor: UIColor! {
willSet {
self.layer.borderColor = newValue.cgColor
self.stackBackground.backgroundColor = newValue
}
}
public func setSegments(_ segments: [NSAttributedString]) {
self.segments = segments
}
#objc private func actSelected(button: UIButton) {
guard let index = self.buttons.index(of: button) else {
print("invalid selection should never happen, would want to handle better than this")
return
}
self.selectedSegementIndex = index
self.sendActions(for: .valueChanged)
}
private func styleButtons() {
for i in 0..<self.buttons.count {
let button = self.buttons[i]
if i == selectedSegementIndex {
button.backgroundColor = self.tintColor
button.titleLabel?.textColor = self.backgroundColor ?? .white
} else {
button.backgroundColor = self.backgroundColor
button.titleLabel?.textColor = self.tintColor
}
}
}
}
extension UIView {
func constrainToBounds(of view: UIView) {
self.translatesAutoresizingMaskIntoConstraints = false
let attrs: [NSLayoutAttribute] = [.leading, .top, .trailing, .bottom]
let constraints = attrs.map { (attr) -> NSLayoutConstraint in
return NSLayoutConstraint(item: self,
attribute: attr,
relatedBy: .equal,
toItem: view,
attribute: attr,
multiplier: 1.0,
constant: 0)
}
NSLayoutConstraint.activate(constraints)
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var customSegment: CustomSegmentedControl!
private var segments: [NSAttributedString] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.customSegment.backgroundColor = .white
self.customSegment.tintColor = .orange
let pizza = createText(title: "Pizza", subTitle: "123K")
let turkey = createText(title: "Turkey Burgers", subTitle: "456.2M")
let gingerAle = createText(title: "Gingerale", subTitle: "789.3B")
self.segments = [pizza, turkey, gingerAle]
self.customSegment.setSegments(self.segments)
self.customSegment.addTarget(self, action: #selector(self.segmentSelectionChanged(control:)), for: .valueChanged)
}
#objc private func segmentSelectionChanged(control: CustomSegmentedControl) {
let segment = self.segments[control.selectedSegementIndex]
print("selected segment = \(segment.string)")
}
func createText(title: String, subTitle: String) -> NSAttributedString {
let titleStr = NSMutableAttributedString(string: "\(title)\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16)])
let subStr = NSAttributedString(string: subTitle, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 10)])
titleStr.append(subStr)
return titleStr
}
}
I know this question has been asked lots of times but none of solutions worked for me.
I have a custom UIView class which I use for displaying alert message. I added UIButton to close the view. However, nothing happens when I tab it.
import UIKit
public class Alert: UIView {
public var image: UIImage?
public var title: String?
public var message: String?
public var closeButtonText: String?
public var dialogBackgroundColor: UIColor = .white
public var dialogTitleTextColor: UIColor = .black
public var dialogMessageTextColor: UIColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
public var dialogImageColor: UIColor = UIColor(red:0.47, green:0.72, blue:0.35, alpha:1.0)
public var overlayColor: UIColor = .black
public var overlayOpacity: CGFloat = 0.66
public var paddingSingleTextOnly: CGFloat = 8
public var paddingTopAndBottom: CGFloat = 24
public var paddingFromSides: CGFloat = 8
public var seperatorHeight: CGFloat = 6
private var height: CGFloat = 0
private var width: CGFloat = 0
private var maxSize: CGSize = CGSize()
private let marginFromSides: CGFloat = 80
public lazy var imageSize: CGSize = CGSize(width: 75, height: 75)
public var overlay = false
public var blurOverlay = true
//animation duration
public var duration = 0.33
private var onComplete: (() -> Void)?
#objc public var titleFont: UIFont = UIFont.systemFont(ofSize: 18)
#objc public var messageFont: UIFont = UIFont.systemFont(ofSize: 15)
private lazy var backgroundView: UIView = {
let view = UIView()
view.alpha = 0
return view
}()
public let dialogView: UIView = {
let view = UIView()
view.layer.cornerRadius = 6
view.layer.masksToBounds = true
view.alpha = 0
view.clipsToBounds = true
return view
}()
private lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
return view
}()
public lazy var closeButton: UIButton = {
let button = UIButton()
return button
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
#objc func closeButtonTapped(sender: UIButton){
dismiss()
}
private func calculations() {
height += paddingTopAndBottom
maxSize = CGSize(width: frame.width - marginFromSides * 2, height: frame.height - marginFromSides)
}
public convenience init(title:String, message: String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
self.image = image
}
public convenience init(title:String, image:UIImage) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.image = image
}
public convenience init(title: String, message: String) {
self.init(frame: UIScreen.main.bounds)
self.title = title
self.message = message
}
public convenience init(message: String) {
self.init(frame: UIScreen.main.bounds)
paddingTopAndBottom = paddingSingleTextOnly
paddingFromSides = paddingSingleTextOnly * 2
self.message = message
}
override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func createOverlay() {
backgroundView.frame = frame
backgroundView.backgroundColor = overlayColor
backgroundView.isUserInteractionEnabled = true
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createBlurOverlay() {
backgroundView.frame = frame
//Blur Effect
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = frame
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundView.addSubview(blurEffectView)
addSubview(backgroundView)
if let window = UIApplication.shared.keyWindow {
window.addSubview(backgroundView)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
window.view.addSubview(self)
}
}
private func createTitle(title: String) {
titleLabel.font = titleFont
titleLabel.text = title
titleLabel.frame.origin.y = height + 2
let titleLabelSize = titleLabel.sizeThatFits(maxSize)
handleSize(size: titleLabelSize)
titleLabel.frame.size = titleLabelSize
titleLabel.textColor = self.dialogTitleTextColor
dialogView.addSubview(titleLabel)
}
private func createMessage(message: String) {
messageLabel.font = messageFont
messageLabel.text = message
messageLabel.frame.origin.y = height
let messageLabelSize = messageLabel.sizeThatFits(maxSize)
messageLabel.frame.size = messageLabelSize
messageLabel.textColor = self.dialogMessageTextColor
handleSize(size: messageLabelSize)
dialogView.addSubview(messageLabel)
}
private func createImage(image: UIImage) {
imageView.image = image.withRenderingMode(.alwaysTemplate)
imageView.frame.origin.y = height
imageView.frame.size = imageSize
imageView.tintColor = self.dialogImageColor
handleSize(size: imageSize)
dialogView.addSubview(imageView)
}
private func createButton(){
closeButton.setTitle("Close", for: .normal)
closeButton.tintColor = UIColor.white
closeButton.frame.origin.y = height + 20
let closeButtonSize = CGSize(width: width - 60, height: 60)
closeButton.frame.size = closeButtonSize
closeButton.layer.cornerRadius = 6
closeButton.backgroundColor = Color.NavigationBar.tintColor
closeButton.isUserInteractionEnabled = true
handleSize(size: closeButtonSize)
dialogView.addSubview(closeButton)
}
private func createDialog() {
centerAll()
height += paddingTopAndBottom
dialogView.frame.size = CGSize(width: width, height: height)
dialogView.backgroundColor = self.dialogBackgroundColor
dialogView.isUserInteractionEnabled = true
addSubview(dialogView)
self.dialogView.center = self.center
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
if let window = UIApplication.shared.keyWindow {
window.addSubview(dialogView)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
} else if let window = UIApplication.shared.delegate?.window??.rootViewController {
UIApplication.topViewController()?.view.addSubview(self)
window.view.addSubview(self)
closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:)), for: .touchUpInside)
}
}
private func centerAll() {
if ((messageLabel.text) != nil) {
messageLabel.frame.origin.x = (width - messageLabel.frame.width) / 2
}
if ((titleLabel.text) != nil) {
titleLabel.frame.origin.x = (width - titleLabel.frame.width) / 2
}
if ((imageView.image) != nil) {
imageView.frame.origin.x = (width - imageView.frame.width) / 2
}
closeButton.frame.origin.x = (width - closeButton.frame.width) / 2
}
private func handleSize(size: CGSize) {
if width < size.width + paddingFromSides * 2 {
width = size.width + paddingFromSides * 2
}
if paddingTopAndBottom != paddingSingleTextOnly {
height += seperatorHeight
}
height += size.height
}
private func showAnimation() {
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = self.overlayOpacity
self.dialogView.transform = CGAffineTransform(scaleX: 1, y: 1)
}
self.dialogView.alpha = 1
})
}
public func show() {
if let complete = onComplete {
self.onComplete = complete
}
calculations()
if self.overlay {
if blurOverlay {
createBlurOverlay()
} else {
createOverlay()
}
}
if let img = image {
createImage(image: img)
}
if let title = title {
createTitle(title: title)
}
if let message = message {
createMessage(message: message)
}
createButton()
createDialog()
showAnimation()
}
public func dismiss(){
UIView.animate(withDuration: duration, animations: {
if self.overlay {
self.backgroundView.alpha = 0
}
self.dialogView.transform = CGAffineTransform(scaleX: 1.15, y: 1.15)
self.dialogView.alpha = 0
}, completion: { (completed) in
self.dialogView.removeFromSuperview()
if (self.overlay)
{
self.backgroundView.removeFromSuperview()
}
self.removeFromSuperview()
if let completionHandler = self.onComplete {
completionHandler()
}
})
}
}
How I create the alert;
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
If I declare target inside UIViewController (Where I create this UIView) as
Alert.closeButton.addTarget(self, action: #selector(closeButtonTapped(sender:), for: .touchUPInside)
and create function inside UIViewController It is working. I can't figure out why It doesn't work when in custom class.
So my question is that how can close the alert view when tabbed the button?
I tried below solution but didn't work for me;
UIButton target action inside custom class
Assuming these lines are inside a function - such as due to a button tap:
#IBAction func didTap(_ sender: Any) {
let alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert.show()
}
You are creating an instance of your Alert class, calling the .show() function inside it, and then it goes out of scope.
So, as soon as that function exists, alert no longer exists, and no code inside it can run.
You need to have a class-level variable to hold onto it while it is displayed:
class MyViewController: UIViewController {
var alert: Alert?
#IBAction func didTap(_ sender: Any) {
alert = Alert(title: "hata",message: "hata mesajı ekrana basıldı", image: #imageLiteral(resourceName: "error"))
alert?.show()
}
}
Here is a demonstration of the "Wrong Way" and the "Right Way" to handle your Alert view: https://github.com/DonMag/EmreTest
Look this code
1- Call IBAction inside UIView Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
self.view.addSubview(alert)
}
}
2- Call IBAction inside UIViewController Class
import UIKit
public class Alert: UIView {
public lazy var closeButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
return button
}()
func createDialog() {
// closeButton.addTarget(self, action: #selector(self.closeButtonTapped(sender:)), for: .touchUpInside)
self.addSubview(closeButton)
}
#objc func closeButtonTapped(sender: UIButton){
print("Call 1")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = Alert(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
alert.createDialog()
alert.closeButton.addTarget(self, action: #selector(self.closeButtonTappedController(_:)), for: .touchUpInside)
self.view.addSubview(alert)
}
#IBAction func closeButtonTappedController(_ sender:UIButton){
print("Call 2")
}
}
Sorry I am new to swift and I have an imageView.
I can see my image in imageView.
I want to be able to zoom the image in and out. But I also fail to change my image size. What's wrong with my code? Sorry,I already find many solution to fix my problem. I don't know how to fix it. Should I add some gestureRecognizer or anything else?
Thank for answer.
This is code:
import UIKit
import SnapKit
class ImageViewController: UIViewController, UIScrollViewDelegate {
var url:String = ""
var id:String = ""
var scrollView:UIScrollView = { () -> UIScrollView in
let ui = UIScrollView()
ui.backgroundColor = UIColor.black
ui.minimumZoomScale = 1.0
ui.maximumZoomScale = 6.0
return ui
}()
var toolbar:UIToolbar = { () -> UIToolbar in
let ui = UIToolbar()
ui.barTintColor = defaultNavBackgroundColor
ui.clipsToBounds = true
ui.isTranslucent = false
ui.isUserInteractionEnabled = true
return ui
}()
var photoImageView:UIImageView = { () -> UIImageView in
let ui = UIImageView()
ui.backgroundColor = UIColor.clear
ui.layer.masksToBounds = true
ui.contentMode = .scaleAspectFit
ui.isUserInteractionEnabled = true
return ui
}()
var buttonDownload:UIBarButtonItem = { () -> UIBarButtonItem in
let ui = UIBarButtonItem()
ui.style = .plain
return ui
}()
override func loadView() {
super.loadView()
view.backgroundColor = UIColor.black
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// navigationItem.title = name
self.automaticallyAdjustsScrollViewInsets = false
imageFromUrl(imageView: photoImageView, url: url, chatroomId: id,isResize: false)
self.buttonDownload.action = #selector(btnDownloadClicked(sender:))
scrollView.delegate = self
scrollView.contentSize = (self.photoImageView.image!.size)
photoImageView.center = scrollView.center
loadContent()
loadVFL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func loadContent() {
toolbar.sizeToFit()
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
buttonDownload = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: nil)
toolbar.items = [flexSpace,buttonDownload]
view.addSubview(scrollView)
view.addSubview(toolbar)
scrollView.addSubview(photoImageView)
}
func loadVFL() {
scrollView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalTo(toolbar.snp.top)
make.left.equalToSuperview()
make.right.equalToSuperview()
}
photoImageView.snp.makeConstraints { (make) -> Void in
make.top.equalToSuperview()
make.bottom.equalToSuperview()
make.left.equalToSuperview()
make.right.equalToSuperview()
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
}
toolbar.snp.makeConstraints { (make) -> Void in
make.width.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(44)
}
}
func btnDownloadClicked(sender:UIBarButtonItem) {
print("press")
}
// MARK: - UIScrollViewDelegate
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return photoImageView
}
}
image like this