I am adding some additional functionality to an existing App. The App controls various industrial hardware devices. We would like to add a sub section where the user selects the appropriate device and is then presented with instructions that are tailored to that specific device.
I would like the user to be able to swipe through these instructions as per the orange sections of the attached image.
I know I need to use UIPageController but I am unsure how I then link this up to the various subviews (x, y & z in drawing) so the user could swipe between them.
I could put all of my subclassed UIViews into a horizontal scroll but that doesn't feel right.
Do I need to use an additional view controller for this? If so how would I implement this in the view hierarchy?
class InstructionsQ44 : UIView{
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override required init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
var _steps = 5 //TBC
var pageControl = UIPageControl()
private func setupView()
{
pageControl.translatesAutoresizingMaskIntoConstraints = false
self.pageControl.numberOfPages = _steps
self.pageControl.currentPage = 0
self.addSubview(pageControl)
NSLayoutConstraint.activate([
pageControl.leadingAnchor.constraint(equalTo: self.leadingAnchor),
pageControl.trailingAnchor.constraint(equalTo: self.trailingAnchor),
pageControl.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
pageControl.addTarget(self, action: #selector(self.changePage(sender:)), for: UIControl.Event.valueChanged)
}
#objc private func changePage(sender: AnyObject) -> () {
}
}
how could i add a number of instance of UIView such that I could swipe between them?
Thanks
Related
I have created a custom UIButton class to show the image of the button at the right edge of the screen.
The code is as follows -
class CutomButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
imageView?.translatesAutoresizingMaskIntoConstraints = false
imageView?.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8).isActive = true
imageView?.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
The above code works as expected. However, the title of the button moves to the right as if the imageView was in its original position (to the left of the title).
Can anyone point out how the title of the button can be placed to its original position as if the image of the button is not present?
By default the UIButton class manages this internally but you do have a few options:
Easy Mode: Modify the edge insets defined in the UIButton: titleEdgeInsets and imageEdgeInsets. Look at a different SO post for a previous answer that is similar enough but you will just have to modify it for your needs.
Advanced (but more flexible) Mode: Since you are creating a subclass, you could instead subclass UIControl instead of UIButton. Since UIButton IS a subclass of UIControl, touch events are still registered in the same manner as a UIButton and you can override the isHighlighed/isSelected to control the way the "custom button" looks in the touched state.
I got auto-layout issue with custom UIView which render correctly in iphone XR
as
but wrong in iphone 7s(the "Reset" button is rendered out of bound)
and setting in storyboard with just a viewcontroller(UI), and no specified UIViewController class, for simplification i just want to debug the view so i do not associate any viewcontroller class to it.
p.s Safe-area has been used
and the custom class UI(FilterView) setting:
and the custom class UI(FilterView) code:
override init(frame: CGRect){
super.init(frame: frame)
setUpView()
}
//for IB
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
private func setUpView(){
Bundle.main.loadNibNamed(Constants.NIB_FILTER_VIEW_NAME, owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = []
contentView.translatesAutoresizingMaskIntoConstraints = true
}
Anyone knows what happened here?
You should not be creating the X button and "Sort by" label that way. What you are doing is replicating what a UINavigationController does. If you want that layout then put your view controller inside a UINavigationController and it will make it much much easier to add the X and the "Sort by" label.
Also by doing this you can add a rightBarItem to be your "reset" button. Again, you don't have to lay this out. UINavigationController does all that for you.
I currently have a UITableView in which each cell contains a list of users. I currently have it setup using a UITextView and line break after each name. I then set the cell height accordingly to how many people are in the group.This is working great but now I want to add profile pictures by each use. Here are two possible solutions I have come up with.
A tableview inside each table view cell
Using a method like this in order to add the image in as text and keep the current textview. I believe this would be the better/ more efficient option.
Which method of these two or other option would be the right way/ or most efficient way of going about doing this.
UPDATE : I am trying to implement a solution from below here is what I have tried but I am seeing nothing when running
ADDING STACK VIEW IN STORYBOARD
CREATING USER VIEW CLASS
import UIKit
#available(iOS 9.0, *)
class UserView: UIStackView {
var name: String?
var image: UIImage? // OR var imageName: String?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
axis = .vertical
let imageView = UIImageView(image: image)
let nameLabel = UILabel()
nameLabel.text = name
addArrangedSubview(imageView)
addArrangedSubview(nameLabel)
}
}
CODE IN CELL FOR ROW AT INDEX PATH
cell.mainStackView.frame = CGRect(x: 0, y: 0, width: cell.cellColor.frame.width, height: cell.cellColor.frame.height)
for i in 0..<joinedArray.count {
let user = UserView()
user.name = joinedArray[i]
cell.mainStackView.addArrangedSubview(user)
}
I would prefer UIStackView inside a UITableViewCell and UIStackView as a subview for each user entry
Something like below mentioned
class UserView: UIStackView {
var name: String?
var image: UIImage? // OR var imageName: String?
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
axis = .vertical
let imageView = UIImageView(image: image)
let nameLabel = UILabel()
nameLabel.text = name
addArrangedSubview(imageView)
addArrangedSubview(nameLabel)
}
}
Instead of putting a UITableView inside a UITableViewCell, you could try to put a UICollectionView in a UITableViewCell. Technically it's the same but with the collection view you get more versatility in the way you can have your images displayed in different layouts (assuming that each UICollectionViewCell takes a UIImageView for each respective image). Ash Furrow has a really nice blog post on this as well as the respective GitHub rep.
Im building my views programatically, but need to refer to the views in my controller for setting properties. I cant see a way to achieve this?
I have a reference to the view as of course the controller inits it, but then i cant access properties for example trying self.view.textField as id thought i would.
How do I achieve this? And vice versa how do I do things like setting the views textField to use the controller as its delegate to handle its input?
First time ive not used storyboard where all this was simpler but messier, appreciate any clarification on these issues
It's easy.
Initialize your UI elements directly in UIViewController.
For example:
private let yourLabel: UILabel = {
let label = UILabel()
label.text = "Text"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
view.addSubview(yourLabel)
// Setup constraints for yourLabel
}
Then access yourLabel anywhere in the controller code. It works for all the UI components.
NOTE!
You can setup all the views not in the controller, but in a separate view. And then add only one view (with other subviews inside) to the controller. You will have access to all subviews using this one view. But the way how you create UI components in the separate view will be the same:
let yourSubView: UILabel = { // any UI component
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
Example of a custom separate UIView:
class CustomView: UIView { // Separate view. Doesn't make the controller massive
let yourSubView: UILabel = { // any UI component
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setupViews() {
addSubview(yourSubView)
// Setup yourSubView constraints
}
}
Then you can add CustomView to UIViewController and access all the subviews of CustomView.
When the app run in Simulator it works but in Storyboard can’t see the preview, why?
Swift code:
Simulator and Storyboard:
Can’t see the custom view background color and when drag UIButton object into custom view doesn’t see the real position(x, y) and its background color.
In Android Studio when you add an object (custom view) in layout.xml file you can see the preview automatically, is it possible to do the same thing in Xcode?
TL;DR; Call in prepareForInterfaceBuilder
// Call The Custom Setup Here
override func prepareForInterfaceBuilder() {
setupView()
}
Calling in layoutSubviews also works, but is called multiples times in runtime, prepareForInterfaceBuilder is called only for Designables Changes, and only with this purpose.
Long Code:
#IBDesignable
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func prepareForInterfaceBuilder() {
setupView()
}
func InitChildPosition() {
var i = 1
for _view in self.subviews {
if _view is UIButton {
_view.center.x = (_view.bounds.width / 2)
_view.center.y = (_view.bounds.height / 2)
}
if _view is UIButton && i == 2 {
_view.center.x = self.bounds.width - (_view.bounds.width / 2)
_view.center.y = self.bounds.height - (_view.bounds.height / 2)
_view.backgroundColor = UIColor.darkGray
}
i += 1
}
}
func setupView() {
self.backgroundColor = UIColor.black
InitChildPosition()
}
}
Call your CustomSetup() from init(frame:)
EDIT
Interface Builder uses init(frame:) to create and position views in storyboards.
You are modifying the subviews of CustomView in CustomSetup() which is called from the initializer. Your view does not have any button subviews in that case, because subviews can only be added after the view has been initialized.
You need to defer calling CustomSetup() to a later point, awakeFromNib() should work in your case.
Better yet, remove CustomSetup() completely and just style your UIButtons in the Interface Builder. You will get the same results without all the complexity.